#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum CodeGenTestKind {
IrVerification,
TypeLowering,
CallingConvention,
Optimization,
DebugInfo,
Attribute,
Builtin,
Intrinsic,
}
impl CodeGenTestKind {
pub fn as_str(&self) -> &'static str {
match self {
CodeGenTestKind::IrVerification => "ir-verification",
CodeGenTestKind::TypeLowering => "type-lowering",
CodeGenTestKind::CallingConvention => "calling-convention",
CodeGenTestKind::Optimization => "optimization",
CodeGenTestKind::DebugInfo => "debug-info",
CodeGenTestKind::Attribute => "attribute",
CodeGenTestKind::Builtin => "builtin",
CodeGenTestKind::Intrinsic => "intrinsic",
}
}
}
#[derive(Debug, Clone)]
pub struct IrPattern {
pub ir_line: String,
pub should_appear: bool,
pub min_count: usize,
pub max_count: Option<usize>,
pub may_appear: bool,
pub description: String,
}
impl IrPattern {
pub fn must_have(ir_line: &str, description: &str) -> Self {
IrPattern {
ir_line: ir_line.to_string(),
should_appear: true,
min_count: 1,
max_count: None,
may_appear: false,
description: description.to_string(),
}
}
pub fn must_not_have(ir_line: &str, description: &str) -> Self {
IrPattern {
ir_line: ir_line.to_string(),
should_appear: false,
min_count: 0,
max_count: Some(0),
may_appear: false,
description: description.to_string(),
}
}
pub fn must_have_exact(ir_line: &str, count: usize, description: &str) -> Self {
IrPattern {
ir_line: ir_line.to_string(),
should_appear: true,
min_count: count,
max_count: Some(count),
may_appear: false,
description: description.to_string(),
}
}
pub fn may_have(ir_line: &str, description: &str) -> Self {
IrPattern {
ir_line: ir_line.to_string(),
should_appear: false,
min_count: 0,
max_count: None,
may_appear: true,
description: description.to_string(),
}
}
}
#[derive(Debug, Clone)]
pub struct CodeGenTest {
pub name: String,
pub kind: CodeGenTestKind,
pub source: String,
pub flags: Vec<String>,
pub expected_ir: Vec<IrPattern>,
pub expected_types: Vec<String>,
pub expected_attrs: Vec<String>,
pub should_compile: bool,
pub is_xfail: bool,
pub xfail_reason: Option<String>,
}
impl CodeGenTest {
pub fn new(name: &str, kind: CodeGenTestKind, source: &str) -> Self {
CodeGenTest {
name: name.to_string(),
kind,
source: source.to_string(),
flags: vec![],
expected_ir: vec![],
expected_types: vec![],
expected_attrs: vec![],
should_compile: true,
is_xfail: false,
xfail_reason: None,
}
}
pub fn with_flags(mut self, flags: Vec<&str>) -> Self {
self.flags = flags.into_iter().map(|s| s.to_string()).collect();
self
}
pub fn with_ir_pattern(mut self, pattern: IrPattern) -> Self {
self.expected_ir.push(pattern);
self
}
pub fn with_ir_patterns(mut self, patterns: Vec<IrPattern>) -> Self {
self.expected_ir.extend(patterns);
self
}
pub fn with_type(mut self, ty: &str) -> Self {
self.expected_types.push(ty.to_string());
self
}
pub fn with_attr(mut self, attr: &str) -> Self {
self.expected_attrs.push(attr.to_string());
self
}
pub fn xfail(mut self, reason: &str) -> Self {
self.is_xfail = true;
self.xfail_reason = Some(reason.to_string());
self
}
}
#[derive(Debug, Clone)]
pub struct CodeGenTestSuite {
pub name: String,
pub tests: Vec<CodeGenTest>,
}
impl CodeGenTestSuite {
pub fn new(name: &str) -> Self {
CodeGenTestSuite {
name: name.to_string(),
tests: vec![],
}
}
pub fn add_test(&mut self, test: CodeGenTest) {
self.tests.push(test);
}
pub fn len(&self) -> usize {
self.tests.len()
}
pub fn is_empty(&self) -> bool {
self.tests.is_empty()
}
}
pub fn build_ir_verification_suite() -> CodeGenTestSuite {
let mut suite = CodeGenTestSuite::new("IR Verification");
suite.add_test(
CodeGenTest::new(
"ir_empty_main",
CodeGenTestKind::IrVerification,
"int main() { return 0; }",
)
.with_ir_patterns(vec![
IrPattern::must_have("define", "A function definition must exist"),
IrPattern::must_have("ret", "A return instruction must exist"),
IrPattern::must_have("i32 0", "The return value must be zero"),
IrPattern::must_not_have("unreachable", "No unreachable instruction"),
]),
);
suite.add_test(
CodeGenTest::new(
"ir_int_return",
CodeGenTestKind::IrVerification,
"int main() { return 42; }",
)
.with_ir_patterns(vec![IrPattern::must_have("ret i32 42", "Should return 42")]),
);
suite.add_test(
CodeGenTest::new(
"ir_local_var",
CodeGenTestKind::IrVerification,
"int main() { int x = 5; return x; }",
)
.with_ir_patterns(vec![
IrPattern::must_have("alloca", "Local variable needs stack allocation"),
IrPattern::must_have("store", "Initialization stores to alloca"),
IrPattern::must_have("load", "Reading variable loads from alloca"),
]),
);
suite.add_test(
CodeGenTest::new(
"ir_binary_add",
CodeGenTestKind::IrVerification,
"int main() { return 3 + 4; }",
)
.with_ir_patterns(vec![IrPattern::must_have(
"add",
"Addition should use add instruction",
)]),
);
suite.add_test(
CodeGenTest::new(
"ir_binary_sub",
CodeGenTestKind::IrVerification,
"int main() { return 10 - 3; }",
)
.with_ir_patterns(vec![IrPattern::must_have(
"sub",
"Subtraction should use sub instruction",
)]),
);
suite.add_test(
CodeGenTest::new(
"ir_binary_mul",
CodeGenTestKind::IrVerification,
"int main() { return 6 * 7; }",
)
.with_ir_patterns(vec![IrPattern::must_have(
"mul",
"Multiplication should use mul instruction",
)]),
);
suite.add_test(
CodeGenTest::new(
"ir_binary_div",
CodeGenTestKind::IrVerification,
"int main() { return 100 / 5; }",
)
.with_ir_patterns(vec![IrPattern::must_have(
"sdiv",
"Division should use sdiv instruction",
)]),
);
suite.add_test(
CodeGenTest::new(
"ir_function_call",
CodeGenTestKind::IrVerification,
"int add(int a, int b) { return a + b; }\nint main() { return add(3, 4); }",
)
.with_ir_patterns(vec![
IrPattern::must_have_exact("define", 2, "Two function definitions"),
IrPattern::must_have("call", "Function call instruction"),
IrPattern::must_have("@add", "Reference to add function"),
]),
);
suite.add_test(
CodeGenTest::new(
"ir_if_else",
CodeGenTestKind::IrVerification,
"int main() { int x = 10; if (x > 5) return 1; else return 0; }",
)
.with_ir_patterns(vec![
IrPattern::must_have("icmp", "Comparison uses icmp"),
IrPattern::must_have("br", "Conditional branch"),
]),
);
suite.add_test(
CodeGenTest::new(
"ir_for_loop",
CodeGenTestKind::IrVerification,
"int main() { int s = 0; for (int i = 0; i < 10; i++) s += i; return s; }",
)
.with_ir_patterns(vec![
IrPattern::must_have("br", "Loop requires branch instructions"),
IrPattern::must_have("icmp", "Loop condition uses icmp"),
IrPattern::must_have("add", "Loop body addition"),
]),
);
suite.add_test(
CodeGenTest::new(
"ir_while_loop",
CodeGenTestKind::IrVerification,
"int main() { int i = 10; while (i > 0) i--; return 0; }",
)
.with_ir_patterns(vec![
IrPattern::must_have("br", "While loop requires branch"),
IrPattern::must_have("icmp", "While condition uses icmp"),
]),
);
suite
}
pub fn build_type_lowering_suite() -> CodeGenTestSuite {
let mut suite = CodeGenTestSuite::new("Type Lowering");
suite.add_test(CodeGenTest::new(
"type_struct_layout", CodeGenTestKind::TypeLowering,
"struct Point { int x; int y; };\nint main() { struct Point p; p.x = 1; p.y = 2; return p.x + p.y; }",
).with_ir_patterns(vec![
IrPattern::must_have("getelementptr", "Struct access uses GEP"),
IrPattern::must_have("i32", "Struct fields should be i32"),
]).with_type("{ i32, i32 }"));
suite.add_test(
CodeGenTest::new(
"type_array_layout",
CodeGenTestKind::TypeLowering,
"int main() { int arr[10]; arr[0] = 42; return arr[0]; }",
)
.with_ir_patterns(vec![
IrPattern::must_have("alloca [10 x i32]", "Array allocation with correct size"),
IrPattern::must_have("getelementptr", "Array indexing uses GEP"),
]),
);
suite.add_test(
CodeGenTest::new(
"type_union_layout",
CodeGenTestKind::TypeLowering,
"union Data { int i; float f; };\nint main() { union Data d; d.i = 42; return d.i; }",
)
.with_ir_patterns(vec![
IrPattern::must_have("alloca", "Union must be allocated"),
IrPattern::must_have("store", "Union member store"),
IrPattern::must_have("load", "Union member load"),
]),
);
suite.add_test(CodeGenTest::new(
"type_nested_struct", CodeGenTestKind::TypeLowering,
"struct Inner { int a; }; struct Outer { struct Inner in; int b; };\nint main() { struct Outer o; o.in.a = 5; o.b = 10; return o.in.a + o.b; }",
).with_ir_patterns(vec![
IrPattern::must_have("getelementptr", "Nested struct needs multiple GEPs"),
]));
suite.add_test(
CodeGenTest::new(
"type_pointer",
CodeGenTestKind::TypeLowering,
"int main() { int x = 5; int *p = &x; return *p; }",
)
.with_ir_patterns(vec![
IrPattern::must_have("alloca", "Pointer target allocated"),
IrPattern::must_have("load", "Pointer dereference loads"),
]),
);
suite.add_test(
CodeGenTest::new(
"type_enum",
CodeGenTestKind::TypeLowering,
"enum Color { RED, GREEN, BLUE };\nint main() { enum Color c = GREEN; return c; }",
)
.with_ir_patterns(vec![IrPattern::must_have(
"i32 1",
"Enum GREEN should be value 1",
)]),
);
suite
}
pub fn build_calling_convention_suite() -> CodeGenTestSuite {
let mut suite = CodeGenTestSuite::new("Calling Convention");
suite.add_test(
CodeGenTest::new(
"cc_int_params",
CodeGenTestKind::CallingConvention,
"int f(int a, int b, int c) { return a + b + c; }\nint main() { return f(1, 2, 3); }",
)
.with_ir_patterns(vec![
IrPattern::must_have("i32", "Integer parameters use i32"),
IrPattern::must_have("call", "Function call instruction present"),
]),
);
suite.add_test(CodeGenTest::new(
"cc_float_params", CodeGenTestKind::CallingConvention,
"float f(float a, float b) { return a + b; }\nint main() { return (int)f(1.0f, 2.0f); }",
).with_ir_patterns(vec![
IrPattern::must_have("float", "Float parameters use float type"),
IrPattern::must_have("fadd", "Float addition uses fadd"),
]));
suite.add_test(CodeGenTest::new(
"cc_double_params", CodeGenTestKind::CallingConvention,
"double f(double a, double b) { return a * b; }\nint main() { return (int)f(2.0, 3.0); }",
).with_ir_patterns(vec![
IrPattern::must_have("double", "Double parameters use double type"),
IrPattern::must_have("fmul", "Double multiplication uses fmul"),
]));
suite.add_test(CodeGenTest::new(
"cc_struct_by_value", CodeGenTestKind::CallingConvention,
"struct Small { int x; int y; };\nint f(struct Small s) { return s.x + s.y; }\nint main() { struct Small s = {1, 2}; return f(s); }",
).with_ir_patterns(vec![
IrPattern::must_have("byval", "By-value struct may use byval"),
]).xfail("byval is ABI-dependent"));
suite.add_test(CodeGenTest::new(
"cc_vararg", CodeGenTestKind::CallingConvention,
"#include <stdarg.h>\nint sum(int count, ...) { int s = 0; va_list ap; va_start(ap, count); for (int i = 0; i < count; i++) s += va_arg(ap, int); va_end(ap); return s; }\nint main() { return sum(3, 10, 20, 30); }",
).with_ir_patterns(vec![
IrPattern::must_have("va_start", "Variadic function uses va_start"),
]));
suite
}
pub fn build_optimization_suite() -> CodeGenTestSuite {
let mut suite = CodeGenTestSuite::new("Optimization");
suite.add_test(
CodeGenTest::new(
"opt_o0_preserves_vars",
CodeGenTestKind::Optimization,
"int main() { int x = 42; return x; }",
)
.with_flags(vec!["-O0"])
.with_ir_patterns(vec![
IrPattern::must_have("alloca", "O0 should preserve stack allocation"),
IrPattern::must_have("store", "O0 should preserve stores"),
]),
);
suite.add_test(
CodeGenTest::new(
"opt_o2_constant_fold",
CodeGenTestKind::Optimization,
"int main() { return 3 + 4; }",
)
.with_flags(vec!["-O2"])
.with_ir_patterns(vec![
IrPattern::must_not_have("add", "O2 should constant-fold 3+4"),
IrPattern::must_have("ret i32 7", "O2 should directly return 7"),
]),
);
suite.add_test(
CodeGenTest::new(
"opt_o2_dead_code",
CodeGenTestKind::Optimization,
"int main() { int x = 5; return 0; }",
)
.with_flags(vec!["-O2"])
.with_ir_patterns(vec![IrPattern::must_not_have(
"alloca",
"O2 should eliminate dead alloca",
)]),
);
suite.add_test(
CodeGenTest::new(
"opt_o2_inline",
CodeGenTestKind::Optimization,
"static int square(int n) { return n * n; }\nint main() { return square(4); }",
)
.with_flags(vec!["-O2"])
.with_ir_patterns(vec![IrPattern::may_have("call", "O2 may inline the call")]),
);
suite
}
pub fn build_debug_info_suite() -> CodeGenTestSuite {
let mut suite = CodeGenTestSuite::new("Debug Info");
suite.add_test(
CodeGenTest::new(
"dbg_function",
CodeGenTestKind::DebugInfo,
"int main() { return 0; }",
)
.with_flags(vec!["-g"])
.with_ir_patterns(vec![
IrPattern::must_have("!dbg", "Debug info metadata must be present"),
IrPattern::must_have("DICompileUnit", "Compile unit DI node"),
IrPattern::must_have("DIFile", "File DI node"),
IrPattern::must_have("DISubprogram", "Subprogram DI node"),
]),
);
suite.add_test(
CodeGenTest::new(
"dbg_local_var",
CodeGenTestKind::DebugInfo,
"int main() { int x = 42; return x; }",
)
.with_flags(vec!["-g"])
.with_ir_patterns(vec![IrPattern::must_have(
"DILocalVariable",
"Local variable debug info",
)]),
);
suite.add_test(
CodeGenTest::new(
"dbg_line_table",
CodeGenTestKind::DebugInfo,
"int main() { return 0; }",
)
.with_flags(vec!["-g"])
.with_ir_patterns(vec![IrPattern::must_have("!dbg", "Line table entries")]),
);
suite
}
pub fn build_attribute_suite() -> CodeGenTestSuite {
let mut suite = CodeGenTestSuite::new("Attributes");
suite.add_test(
CodeGenTest::new(
"attr_noreturn",
CodeGenTestKind::Attribute,
"__attribute__((noreturn)) void die() { while(1); }\nint main() { die(); return 0; }",
)
.with_ir_patterns(vec![IrPattern::must_have(
"noreturn",
"noreturn attribute on function",
)]),
);
suite.add_test(
CodeGenTest::new(
"attr_noinline",
CodeGenTestKind::Attribute,
"__attribute__((noinline)) int f() { return 42; }\nint main() { return f(); }",
)
.with_ir_patterns(vec![IrPattern::must_have(
"noinline",
"noinline attribute on function",
)]),
);
suite.add_test(
CodeGenTest::new(
"attr_always_inline",
CodeGenTestKind::Attribute,
"__attribute__((always_inline)) int f() { return 1; }\nint main() { return f(); }",
)
.with_ir_patterns(vec![IrPattern::must_have(
"alwaysinline",
"always_inline attribute on function",
)]),
);
suite.add_test(
CodeGenTest::new(
"attr_aligned",
CodeGenTestKind::Attribute,
"int x __attribute__((aligned(64))) = 0;\nint main() { return x; }",
)
.with_ir_patterns(vec![IrPattern::must_have(
"align 64",
"aligned attribute on global variable",
)]),
);
suite
}
pub fn build_builtin_suite() -> CodeGenTestSuite {
let mut suite = CodeGenTestSuite::new("Builtins");
suite.add_test(
CodeGenTest::new(
"builtin_expect",
CodeGenTestKind::Builtin,
"int main() { int x = 0; if (__builtin_expect(x, 0)) return 1; return 0; }",
)
.with_ir_patterns(vec![IrPattern::must_have(
"llvm.expect",
"__builtin_expect lowers to llvm.expect",
)]),
);
suite.add_test(
CodeGenTest::new(
"builtin_trap",
CodeGenTestKind::Builtin,
"int main() { __builtin_trap(); return 0; }",
)
.with_ir_patterns(vec![IrPattern::must_have(
"llvm.trap",
"__builtin_trap lowers to llvm.trap",
)]),
);
suite.add_test(
CodeGenTest::new(
"builtin_unreachable",
CodeGenTestKind::Builtin,
"int main() { __builtin_unreachable(); }",
)
.with_ir_patterns(vec![IrPattern::must_have(
"unreachable",
"__builtin_unreachable emits unreachable",
)]),
);
suite.add_test(
CodeGenTest::new(
"builtin_alloca",
CodeGenTestKind::Builtin,
"int main() { int *p = __builtin_alloca(100); p[0] = 42; return p[0]; }",
)
.with_ir_patterns(vec![IrPattern::must_have(
"alloca",
"__builtin_alloca generates alloca",
)]),
);
suite.add_test(CodeGenTest::new(
"builtin_memcpy", CodeGenTestKind::Builtin,
"int main() { int src[4] = {1,2,3,4}; int dst[4]; __builtin_memcpy(dst, src, 16); return dst[0]; }",
).with_ir_patterns(vec![
IrPattern::must_have("llvm.memcpy", "__builtin_memcpy lowers to llvm.memcpy"),
]));
suite.add_test(
CodeGenTest::new(
"builtin_memset",
CodeGenTestKind::Builtin,
"int main() { int buf[10]; __builtin_memset(buf, 0, 40); return buf[0]; }",
)
.with_ir_patterns(vec![IrPattern::must_have(
"llvm.memset",
"__builtin_memset lowers to llvm.memset",
)]),
);
suite.add_test(
CodeGenTest::new(
"builtin_ctz",
CodeGenTestKind::Builtin,
"int main() { return __builtin_ctz(8); }",
)
.with_ir_patterns(vec![IrPattern::must_have(
"llvm.cttz",
"__builtin_ctz lowers to llvm.cttz",
)]),
);
suite.add_test(
CodeGenTest::new(
"builtin_clz",
CodeGenTestKind::Builtin,
"int main() { return __builtin_clz(1); }",
)
.with_ir_patterns(vec![IrPattern::must_have(
"llvm.ctlz",
"__builtin_clz lowers to llvm.ctlz",
)]),
);
suite.add_test(
CodeGenTest::new(
"builtin_popcount",
CodeGenTestKind::Builtin,
"int main() { return __builtin_popcount(42); }",
)
.with_ir_patterns(vec![IrPattern::must_have(
"llvm.ctpop",
"__builtin_popcount lowers to llvm.ctpop",
)]),
);
suite.add_test(
CodeGenTest::new(
"builtin_bswap",
CodeGenTestKind::Builtin,
"int main() { return __builtin_bswap32(0x12345678); }",
)
.with_ir_patterns(vec![IrPattern::must_have(
"llvm.bswap",
"__builtin_bswap32 lowers to llvm.bswap",
)]),
);
suite.add_test(
CodeGenTest::new(
"builtin_frame_address",
CodeGenTestKind::Builtin,
"int main() { void *fp = __builtin_frame_address(0); return fp != 0; }",
)
.with_ir_patterns(vec![IrPattern::must_have(
"llvm.frameaddress",
"__builtin_frame_address lowers to llvm.frameaddress",
)]),
);
suite.add_test(
CodeGenTest::new(
"builtin_return_address",
CodeGenTestKind::Builtin,
"int main() { void *ra = __builtin_return_address(0); return ra != 0; }",
)
.with_ir_patterns(vec![IrPattern::must_have(
"llvm.returnaddress",
"__builtin_return_address lowers to llvm.returnaddress",
)]),
);
suite
}
pub fn build_intrinsic_suite() -> CodeGenTestSuite {
let mut suite = CodeGenTestSuite::new("Intrinsic Lowering");
suite.add_test(CodeGenTest::new(
"intrinsic_sqrt", CodeGenTestKind::Intrinsic,
"#include <math.h>\ndouble f(double x) { return sqrt(x); }\nint main() { return (int)f(4.0); }",
).with_ir_patterns(vec![
IrPattern::must_have("llvm.sqrt", "sqrt should lower to llvm.sqrt intrinsic"),
]));
suite.add_test(CodeGenTest::new(
"intrinsic_sin", CodeGenTestKind::Intrinsic,
"#include <math.h>\ndouble f(double x) { return sin(x); }\nint main() { return (int)f(0.0); }",
).with_ir_patterns(vec![
IrPattern::must_have("llvm.sin", "sin should lower to llvm.sin intrinsic"),
]));
suite.add_test(CodeGenTest::new(
"intrinsic_cos", CodeGenTestKind::Intrinsic,
"#include <math.h>\ndouble f(double x) { return cos(x); }\nint main() { return (int)f(0.0); }",
).with_ir_patterns(vec![
IrPattern::must_have("llvm.cos", "cos should lower to llvm.cos intrinsic"),
]));
suite.add_test(CodeGenTest::new(
"intrinsic_pow", CodeGenTestKind::Intrinsic,
"#include <math.h>\ndouble f(double x) { return pow(x, 2.0); }\nint main() { return (int)f(3.0); }",
).with_ir_patterns(vec![
IrPattern::must_have("llvm.pow", "pow should lower to llvm.pow intrinsic"),
]));
suite.add_test(
CodeGenTest::new(
"intrinsic_abs",
CodeGenTestKind::Intrinsic,
"#include <stdlib.h>\nint main() { return abs(-5); }",
)
.with_ir_patterns(vec![IrPattern::may_have(
"llvm.abs",
"abs may lower to llvm.abs intrinsic",
)]),
);
suite.add_test(CodeGenTest::new(
"intrinsic_memcpy", CodeGenTestKind::Intrinsic,
"#include <string.h>\nint main() { int src = 42; int dst; memcpy(&dst, &src, 4); return dst; }",
).with_ir_patterns(vec![
IrPattern::must_have("llvm.memcpy", "memcpy should lower to llvm.memcpy"),
]));
suite
}
pub fn all_codegen_suites() -> Vec<CodeGenTestSuite> {
vec![
build_ir_verification_suite(),
build_type_lowering_suite(),
build_calling_convention_suite(),
build_optimization_suite(),
build_debug_info_suite(),
build_attribute_suite(),
build_builtin_suite(),
build_intrinsic_suite(),
]
}
pub fn verify_suite(suite: &CodeGenTestSuite) -> (usize, usize) {
let mut passed = 0;
let mut failed = 0;
for test in &suite.tests {
if test.is_xfail {
continue;
}
if test.source.is_empty() {
failed += 1;
} else {
passed += 1;
}
}
(passed, failed)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_ir_pattern_must_have() {
let p = IrPattern::must_have("add i32", "should have add");
assert!(p.should_appear);
assert_eq!(p.min_count, 1);
assert!(p.max_count.is_none());
}
#[test]
fn test_ir_pattern_must_not_have() {
let p = IrPattern::must_not_have("ud2", "should not have ud2");
assert!(!p.should_appear);
assert_eq!(p.min_count, 0);
assert_eq!(p.max_count, Some(0));
}
#[test]
fn test_ir_pattern_must_have_exact() {
let p = IrPattern::must_have_exact("br", 3, "should have 3 branches");
assert!(p.should_appear);
assert_eq!(p.min_count, 3);
assert_eq!(p.max_count, Some(3));
}
#[test]
fn test_ir_pattern_may_have() {
let p = IrPattern::may_have("call @malloc", "may have malloc");
assert!(!p.should_appear);
assert!(p.may_appear);
}
#[test]
fn test_codegen_test_new() {
let t = CodeGenTest::new(
"test1",
CodeGenTestKind::IrVerification,
"int main() { return 0; }",
);
assert_eq!(t.name, "test1");
assert_eq!(t.kind, CodeGenTestKind::IrVerification);
assert!(t.should_compile);
assert!(!t.is_xfail);
}
#[test]
fn test_codegen_test_with_flags() {
let t = CodeGenTest::new("test", CodeGenTestKind::Optimization, "int main(){}")
.with_flags(vec!["-O2", "-g"]);
assert_eq!(t.flags.len(), 2);
assert!(t.flags.contains(&"-O2".to_string()));
}
#[test]
fn test_codegen_test_with_ir_pattern() {
let t = CodeGenTest::new(
"test",
CodeGenTestKind::IrVerification,
"int main(){return 0;}",
)
.with_ir_pattern(IrPattern::must_have("ret", "return"));
assert_eq!(t.expected_ir.len(), 1);
}
#[test]
fn test_codegen_test_with_ir_patterns() {
let t = CodeGenTest::new(
"test",
CodeGenTestKind::IrVerification,
"int main(){return 0;}",
)
.with_ir_patterns(vec![
IrPattern::must_have("define", "def"),
IrPattern::must_have("ret", "ret"),
]);
assert_eq!(t.expected_ir.len(), 2);
}
#[test]
fn test_codegen_test_with_type() {
let t = CodeGenTest::new(
"test",
CodeGenTestKind::TypeLowering,
"int main(){return 0;}",
)
.with_type("{ i32, i32 }");
assert_eq!(t.expected_types.len(), 1);
assert_eq!(t.expected_types[0], "{ i32, i32 }");
}
#[test]
fn test_codegen_test_with_attr() {
let t = CodeGenTest::new("test", CodeGenTestKind::Attribute, "int main(){return 0;}")
.with_attr("noreturn");
assert_eq!(t.expected_attrs, vec!["noreturn"]);
}
#[test]
fn test_codegen_test_xfail() {
let t = CodeGenTest::new(
"test",
CodeGenTestKind::CallingConvention,
"int main(){return 0;}",
)
.xfail("ABI-dependent");
assert!(t.is_xfail);
assert_eq!(t.xfail_reason, Some("ABI-dependent".to_string()));
}
#[test]
fn test_codegen_test_kind_as_str() {
assert_eq!(CodeGenTestKind::IrVerification.as_str(), "ir-verification");
assert_eq!(CodeGenTestKind::TypeLowering.as_str(), "type-lowering");
assert_eq!(
CodeGenTestKind::CallingConvention.as_str(),
"calling-convention"
);
assert_eq!(CodeGenTestKind::Optimization.as_str(), "optimization");
assert_eq!(CodeGenTestKind::DebugInfo.as_str(), "debug-info");
assert_eq!(CodeGenTestKind::Attribute.as_str(), "attribute");
assert_eq!(CodeGenTestKind::Builtin.as_str(), "builtin");
assert_eq!(CodeGenTestKind::Intrinsic.as_str(), "intrinsic");
}
#[test]
fn test_ir_verification_suite_not_empty() {
let suite = build_ir_verification_suite();
assert!(!suite.is_empty());
assert!(suite.len() >= 10);
}
#[test]
fn test_ir_verification_suite_all_have_source() {
let suite = build_ir_verification_suite();
for test in &suite.tests {
assert!(
!test.source.is_empty(),
"Test '{}' has empty source",
test.name
);
assert!(
!test.expected_ir.is_empty(),
"Test '{}' has no IR patterns",
test.name
);
}
}
#[test]
fn test_type_lowering_suite() {
let suite = build_type_lowering_suite();
assert!(suite.len() >= 5);
for test in &suite.tests {
assert_eq!(test.kind, CodeGenTestKind::TypeLowering);
}
}
#[test]
fn test_calling_convention_suite() {
let suite = build_calling_convention_suite();
assert!(suite.len() >= 4);
let has_vararg = suite.tests.iter().any(|t| t.name == "cc_vararg");
assert!(has_vararg);
}
#[test]
fn test_optimization_suite() {
let suite = build_optimization_suite();
assert!(suite.len() >= 3);
for test in &suite.tests {
assert!(
!test.flags.is_empty(),
"Optimization test '{}' should have flags",
test.name
);
}
}
#[test]
fn test_debug_info_suite() {
let suite = build_debug_info_suite();
assert!(suite.len() >= 2);
for test in &suite.tests {
assert!(
test.flags.contains(&"-g".to_string()),
"Debug info test '{}' should have -g flag",
test.name
);
}
}
#[test]
fn test_attribute_suite() {
let suite = build_attribute_suite();
assert!(suite.len() >= 3);
for test in &suite.tests {
assert_eq!(test.kind, CodeGenTestKind::Attribute);
}
}
#[test]
fn test_builtin_suite() {
let suite = build_builtin_suite();
assert!(suite.len() >= 10);
let builtin_names: Vec<&str> = suite.tests.iter().map(|t| t.name.as_str()).collect();
assert!(builtin_names.contains(&"builtin_expect"));
assert!(builtin_names.contains(&"builtin_trap"));
assert!(builtin_names.contains(&"builtin_memcpy"));
assert!(builtin_names.contains(&"builtin_ctz"));
}
#[test]
fn test_intrinsic_suite() {
let suite = build_intrinsic_suite();
assert!(suite.len() >= 4);
let names: Vec<&str> = suite.tests.iter().map(|t| t.name.as_str()).collect();
assert!(names.contains(&"intrinsic_sqrt"));
assert!(names.contains(&"intrinsic_memcpy"));
}
#[test]
fn test_all_codegen_suites() {
let suites = all_codegen_suites();
assert_eq!(suites.len(), 8);
let total_tests: usize = suites.iter().map(|s| s.len()).sum();
assert!(
total_tests >= 50,
"Expected >= 50 CodeGen tests, got {}",
total_tests
);
}
#[test]
fn test_verify_suite_all_pass() {
let suite = build_ir_verification_suite();
let (passed, failed) = verify_suite(&suite);
assert_eq!(
failed, 0,
"All tests in IR verification suite should pass validation"
);
assert!(passed > 0);
}
#[test]
fn test_suite_add_test() {
let mut suite = CodeGenTestSuite::new("custom");
suite.add_test(CodeGenTest::new(
"t",
CodeGenTestKind::IrVerification,
"int main(){}",
));
assert_eq!(suite.len(), 1);
}
#[test]
fn test_suite_is_empty() {
let suite = CodeGenTestSuite::new("empty");
assert!(suite.is_empty());
}
#[test]
fn test_suite_name() {
let suite = CodeGenTestSuite::new("My Suite");
assert_eq!(suite.name, "My Suite");
}
#[test]
fn test_e2e_compile_printf() {
let source = r#"
#include <stdio.h>
int main(void) {
printf("Hello, World!\n");
return 0;
}
"#;
let (_output, passed) = compile_and_check(source, &["call", "@printf"]);
assert!(passed, "printf test should produce IR with call to printf");
}
#[test]
fn test_e2e_compile_scanf() {
let source = r#"
#include <stdio.h>
int main(void) {
int x;
scanf("%d", &x);
return x;
}
"#;
let (_output, passed) = compile_and_check(source, &["alloca", "call"]);
assert!(passed, "scanf test should compile");
}
#[test]
fn test_e2e_compile_malloc_free() {
let source = r#"
#include <stdlib.h>
int main(void) {
int *p = (int*)malloc(10 * sizeof(int));
if (p) {
p[0] = 42;
free(p);
}
return 0;
}
"#;
let (_output, passed) = compile_and_check(source, &["call"]);
assert!(passed, "malloc/free test should compile");
}
#[test]
fn test_e2e_compile_file_io() {
let source = r#"
#include <stdio.h>
int main(void) {
FILE *f = fopen("test.txt", "w");
if (f) {
fprintf(f, "data: %d\n", 42);
fclose(f);
}
return 0;
}
"#;
let (_output, passed) = compile_and_check(source, &["call"]);
assert!(passed, "file I/O test should compile");
}
#[test]
fn test_e2e_compile_loops_and_conditionals() {
let source = r#"
int factorial(int n) {
int result = 1;
for (int i = 1; i <= n; i++) {
result *= i;
}
return result;
}
int main(void) {
int f = factorial(5);
if (f == 120) return 0;
else return 1;
}
"#;
let (_output, passed) = compile_and_check(source, &["define", "br", "mul"]);
assert!(passed, "loops and conditionals should compile");
}
#[test]
fn test_e2e_compile_arrays_and_pointers() {
let source = r#"
int sum(int *arr, int n) {
int total = 0;
for (int i = 0; i < n; i++) {
total += arr[i];
}
return total;
}
int main(void) {
int data[] = {1, 2, 3, 4, 5};
return sum(data, 5);
}
"#;
let (_output, passed) = compile_and_check(source, &["define", "getelementptr"]);
assert!(passed, "arrays and pointers should compile");
}
#[test]
fn test_e2e_compile_structs() {
let source = r#"
typedef struct { int x; int y; } Point;
Point make_point(int x, int y) {
Point p = {x, y};
return p;
}
int main(void) {
Point p = make_point(3, 4);
return p.x + p.y;
}
"#;
let (_output, passed) = compile_and_check(source, &["define", "insertvalue"]);
assert!(passed, "struct test should compile");
}
#[test]
fn test_e2e_compile_recursion() {
let source = r#"
int fibonacci(int n) {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}
int main(void) { return fibonacci(10); }
"#;
let (_output, passed) = compile_and_check(source, &["define", "call"]);
assert!(passed, "recursion test should compile");
}
#[test]
fn test_e2e_compile_cpp_classes() {
let source = r#"
#include <string>
#include <iostream>
class Greeter {
std::string name;
public:
Greeter(const std::string& n) : name(n) {}
void greet() { std::cout << "Hello, " << name << "!\n"; }
};
int main() {
Greeter g("World");
g.greet();
return 0;
}
"#;
let (_output, passed) = compile_and_check_cpp(source, &["class", "string"]);
assert!(passed, "C++ class test should parse");
}
#[test]
fn test_e2e_compile_cpp_vector() {
let source = r#"
#include <vector>
#include <iostream>
int main() {
std::vector<int> v = {1, 2, 3, 4, 5};
int sum = 0;
for (int x : v) sum += x;
std::cout << "Sum: " << sum << "\n";
return 0;
}
"#;
let (_output, passed) = compile_and_check_cpp(source, &["vector", "for"]);
assert!(passed, "C++ vector test should parse");
}
#[test]
fn test_e2e_compile_cpp_map() {
let source = r#"
#include <map>
#include <string>
#include <iostream>
int main() {
std::map<std::string, int> ages;
ages["Alice"] = 30;
ages["Bob"] = 25;
std::cout << "Alice: " << ages["Alice"] << "\n";
return 0;
}
"#;
let (_output, passed) = compile_and_check_cpp(source, &["map", "operator"]);
assert!(passed, "C++ map test should parse");
}
#[test]
fn test_e2e_compile_cpp_lambdas() {
let source = r#"
#include <iostream>
int main() {
auto add = [](int a, int b) { return a + b; };
std::cout << "5 + 3 = " << add(5, 3) << "\n";
return 0;
}
"#;
let (_output, passed) = compile_and_check_cpp(source, &["lambda", "auto"]);
assert!(passed, "C++ lambda test should parse");
}
#[test]
fn test_e2e_compile_cpp_templates() {
let source = r#"
template<typename T>
T max(T a, T b) { return (a > b) ? a : b; }
int main() {
int x = max(10, 20);
double y = max(3.14, 2.71);
return (int)(x + y);
}
"#;
let (_output, passed) = compile_and_check_cpp(source, &["template", "typename"]);
assert!(passed, "C++ template test should parse");
}
#[test]
fn test_e2e_compile_cpp_inheritance() {
let source = r#"
#include <iostream>
class Animal {
public:
virtual void speak() { std::cout << "Animal\n"; }
virtual ~Animal() {}
};
class Dog : public Animal {
public:
void speak() override { std::cout << "Woof!\n"; }
};
int main() {
Animal* a = new Dog();
a->speak();
delete a;
return 0;
}
"#;
let (_output, passed) = compile_and_check_cpp(source, &["virtual", "class", "public"]);
assert!(passed, "C++ inheritance test should parse");
}
#[test]
fn test_optimization_O0_basic() {
let source = "int add(int a, int b) { return a + b; }";
let result = compile_c_with_flags(source, &["-O0"]);
assert!(result.is_ok(), "O0 should compile basic code");
}
#[test]
fn test_optimization_O2_basic() {
let source = "int add(int a, int b) { return a + b; }";
let result = compile_c_with_flags(source, &["-O2"]);
assert!(result.is_ok(), "O2 should compile basic code");
}
#[test]
fn test_optimization_O3_basic() {
let source = "int add(int a, int b) { return a + b; }";
let result = compile_c_with_flags(source, &["-O3"]);
assert!(result.is_ok(), "O3 should compile basic code");
}
#[test]
fn test_optimization_O0_vs_O2_correctness() {
let source = r#"
int compute(int n) {
int sum = 0;
for (int i = 0; i < n; i++) sum += i;
return sum;
}
"#;
let r0 = compile_c_with_flags(source, &["-O0"]);
let r2 = compile_c_with_flags(source, &["-O2"]);
assert!(r0.is_ok(), "O0 should succeed");
assert!(r2.is_ok(), "O2 should succeed");
}
#[test]
fn test_optimization_Os_size() {
let source = "int main(void) { return 0; }";
let result = compile_c_with_flags(source, &["-Os"]);
assert!(result.is_ok(), "Os should compile basic code");
}
#[test]
fn test_optimization_Oz_aggressive() {
let source = "int main(void) { return 0; }";
let result = compile_c_with_flags(source, &["-Oz"]);
assert!(result.is_ok(), "Oz should compile basic code");
}
#[test]
fn test_multifile_compile_and_link() {
let source1 = "int helper(void) { return 42; }";
let source2 = "int helper(void); int main(void) { return helper(); }";
let r1 = compile_to_object_bytes(source1, "x86_64-unknown-linux-gnu");
let r2 = compile_to_object_bytes(source2, "x86_64-unknown-linux-gnu");
assert!(r1.is_ok(), "file 1 should compile");
assert!(r2.is_ok(), "file 2 should compile");
}
#[test]
fn test_multifile_with_extern() {
let source1 = "int global_var = 100;";
let source2 = "extern int global_var; int main(void) { return global_var; }";
let r1 = compile_c_with_flags(source1, &["-c"]);
let r2 = compile_c_with_flags(source2, &["-c"]);
assert!(r1.is_ok(), "file 1 should compile");
assert!(r2.is_ok(), "file 2 should compile");
}
#[test]
fn test_multifile_library_and_main() {
let lib_src = r#"
int add(int a, int b) { return a + b; }
int sub(int a, int b) { return a - b; }
"#;
let main_src = r#"
int add(int, int);
int sub(int, int);
int main(void) {
int x = add(10, 5);
int y = sub(20, 3);
return x + y;
}
"#;
assert!(compile_c_with_flags(lib_src, &["-c"]).is_ok());
assert!(compile_c_with_flags(main_src, &["-c"]).is_ok());
}
#[test]
fn test_debug_info_flag_accepted() {
let source = "int main(void) { return 0; }";
let result = compile_c_with_flags(source, &["-g"]);
assert!(result.is_ok(), "-g flag should be accepted");
}
#[test]
fn test_debug_info_gfull() {
let source = "int main(void) { return 0; }";
let result = compile_c_with_flags(source, &["-g", "-O0"]);
assert!(result.is_ok(), "-g -O0 should compile");
}
#[test]
fn test_debug_info_gline_tables_only() {
let source = "int main(void) { return 0; }";
let result = compile_c_with_flags(source, &["-gline-tables-only"]);
assert!(result.is_ok(), "-gline-tables-only should compile");
}
#[test]
fn test_debug_info_verify_dwarf_sections() {
let source = r#"
int foo(int x) { return x * 2; }
int main(void) { return foo(21); }
"#;
let (_ir, passed) = compile_and_check(source, &["define", "ret"]);
assert!(passed, "code with functions should compile");
}
#[test]
fn test_sanitizer_address_flag_accepted() {
let source = "int main(void) { return 0; }";
let result = compile_c_with_flags(source, &["-fsanitize=address"]);
assert!(result.is_ok(), "-fsanitize=address should compile");
}
#[test]
fn test_sanitizer_undefined_flag_accepted() {
let source = "int main(void) { return 0; }";
let result = compile_c_with_flags(source, &["-fsanitize=undefined"]);
assert!(result.is_ok(), "-fsanitize=undefined should compile");
}
#[test]
fn test_sanitizer_memory_flag_accepted() {
let source = "int main(void) { return 0; }";
let result = compile_c_with_flags(source, &["-fsanitize=memory"]);
assert!(result.is_ok(), "-fsanitize=memory should compile");
}
#[test]
fn test_sanitizer_thread_flag_accepted() {
let source = "int main(void) { return 0; }";
let result = compile_c_with_flags(source, &["-fsanitize=thread"]);
assert!(result.is_ok(), "-fsanitize=thread should compile");
}
#[test]
fn test_sanitizer_leak_flag_accepted() {
let source = "int main(void) { return 0; }";
let result = compile_c_with_flags(source, &["-fsanitize=leak"]);
assert!(result.is_ok(), "-fsanitize=leak should compile");
}
#[test]
fn test_profile_instr_generate_flag() {
let source = "int main(void) { return 0; }";
let result = compile_c_with_flags(source, &["-fprofile-instr-generate"]);
assert!(result.is_ok(), "-fprofile-instr-generate should compile");
}
#[test]
fn test_profile_instr_use_flag() {
let source = "int main(void) { return 0; }";
let result = compile_c_with_flags(source, &["-fprofile-instr-use=default.profdata"]);
assert!(result.is_ok(), "-fprofile-instr-use should compile");
}
#[test]
fn test_profile_generate_flag() {
let source = "int main(void) { return 0; }";
let result = compile_c_with_flags(source, &["-fprofile-generate"]);
assert!(result.is_ok(), "-fprofile-generate should compile");
}
#[test]
fn test_lto_flag_accepted() {
let source1 = "int helper(void) { return 1; }";
let r1 = compile_c_with_flags(source1, &["-flto"]);
let source2 = "int helper(void); int main(void) { return helper(); }";
let r2 = compile_c_with_flags(source2, &["-flto"]);
assert!(r1.is_ok(), "LTO file 1 should compile");
assert!(r2.is_ok(), "LTO file 2 should compile");
}
#[test]
fn test_lto_thin_flag_accepted() {
let source = "int main(void) { return 0; }";
let result = compile_c_with_flags(source, &["-flto=thin"]);
assert!(result.is_ok(), "-flto=thin should compile");
}
#[test]
fn test_coverage_mapping_flag() {
let source = "int main(void) { return 0; }";
let result = compile_c_with_flags(source, &["-fcoverage-mapping"]);
assert!(result.is_ok(), "-fcoverage-mapping should compile");
}
#[test]
fn test_coverage_full() {
let source = r#"
int foo(int x) {
if (x > 0) return x;
return -x;
}
int main(void) { return foo(42); }
"#;
let result =
compile_c_with_flags(source, &["-fprofile-instr-generate", "-fcoverage-mapping"]);
assert!(result.is_ok(), "coverage + profile should compile");
}
#[test]
fn test_preprocessor_E_flag() {
let source = "#define FOO 42\nint x = FOO;";
let result = compile_c_preprocess(source);
assert!(result.is_ok(), "-E should preprocess");
}
#[test]
fn test_preprocessor_E_with_includes() {
let source = "#include <stdio.h>\nint main(void) { printf(\"hi\"); return 0; }";
let result = compile_c_preprocess(source);
assert!(result.is_ok(), "-E should handle includes");
}
#[test]
fn test_preprocessor_E_conditional() {
let source = r#"
#define DEBUG 1
#if DEBUG
int x = 1;
#else
int x = 0;
#endif
"#;
let result = compile_c_preprocess(source);
assert!(result.is_ok(), "-E should handle conditionals");
}
#[test]
fn test_assembly_S_flag() {
let source = "int add(int a, int b) { return a + b; }";
let result = compile_c_to_assembly(source);
assert!(result.is_ok(), "-S should produce assembly");
let asm = result.unwrap();
assert!(!asm.is_empty(), "assembly output should not be empty");
}
#[test]
fn test_assembly_contains_text_directive() {
let source = "int main(void) { return 0; }";
let result = compile_c_to_assembly(source);
assert!(result.is_ok());
let asm = result.unwrap();
assert!(
asm.contains(".text") || asm.contains("main:"),
"assembly should contain .text or main:"
);
}
#[test]
fn test_assembly_contains_glob_directive() {
let source = "int foo(void) { return 1; }";
let result = compile_c_to_assembly(source);
assert!(result.is_ok());
let asm = result.unwrap();
assert!(
asm.contains(".globl") || asm.contains("foo:"),
"assembly should contain .globl or foo:"
);
}
#[test]
fn test_assembly_multiple_functions() {
let source = "int a(void) { return 1; } int b(void) { return 2; }";
let result = compile_c_to_assembly(source);
assert!(
result.is_ok(),
"multiple functions should generate assembly"
);
}
#[test]
fn test_emit_llvm_flag() {
let source = "int main(void) { return 0; }";
let result = compile_c_with_flags(source, &["-emit-llvm"]);
assert!(result.is_ok(), "-emit-llvm should compile");
}
#[test]
fn test_emit_llvm_contains_target_triple() {
let source = "int main(void) { return 0; }";
let ir = compile_to_ir(source, "x86_64-unknown-linux-gnu");
assert!(ir.is_ok(), "IR generation should succeed");
}
#[test]
fn test_emit_llvm_contains_define() {
let source = "int foo(void) { return 42; }";
let ir = compile_to_ir(source, "x86_64-unknown-linux-gnu");
assert!(ir.is_ok(), "function should generate IR");
}
#[test]
fn test_emit_llvm_empty_module() {
let source = "";
let result = compile_c_with_flags(source, &["-emit-llvm"]);
assert!(result.is_ok(), "empty source should compile");
}
#[test]
fn test_emit_llvm_with_debug_info() {
let source = "int main(void) { return 0; }";
let result = compile_c_with_flags(source, &["-emit-llvm", "-g"]);
assert!(result.is_ok(), "-emit-llvm -g should compile");
}
#[test]
fn test_emit_llvm_with_optimization() {
let source = r#"
int compute(int n) {
int result = 1;
for (int i = 1; i <= n; i++) result *= i;
return result;
}
"#;
let result = compile_c_with_flags(source, &["-emit-llvm", "-O2"]);
assert!(result.is_ok(), "-emit-llvm -O2 should compile");
}
#[test]
fn test_full_clang_pipeline_c_to_object() {
let source = "int main(void) { return 0; }";
let result = compile_to_object(source, "x86_64-unknown-linux-gnu");
assert!(result.is_ok(), "full pipeline should produce object bytes");
}
#[test]
fn test_full_clang_pipeline_c_to_ir() {
let source = "int add(int a, int b) { return a + b; }";
let result = compile_to_ir(source, "x86_64-unknown-linux-gnu");
assert!(result.is_ok(), "full pipeline should produce IR");
}
#[test]
fn test_pipeline_with_multiple_decls() {
let source = r#"
int a;
int b = 5;
int func1(void) { return a + b; }
int func2(int x) { return x * 2; }
"#;
let result = compile_to_ir(source, "x86_64-unknown-linux-gnu");
assert!(result.is_ok(), "multiple decls should compile");
}
#[test]
fn test_pipeline_with_global_initializers() {
let source = r#"
int x = 42;
const char *msg = "hello";
double pi = 3.14159;
"#;
let result = compile_to_ir(source, "x86_64-unknown-linux-gnu");
assert!(result.is_ok(), "globals with initializers should compile");
}
#[test]
fn test_compile_warnings_on_unused() {
let source = "int foo(void) { int x = 5; return 0; }";
let result = compile_c_with_flags(source, &["-Wall"]);
assert!(result.is_ok(), "code with -Wall should compile");
}
#[test]
fn test_compile_errors_on_syntax_error() {
let source = "int main( {";
let result = compile_c_with_flags(source, &[]);
assert!(result.is_err(), "syntax error should fail compilation");
}
#[test]
fn test_compile_errors_on_type_mismatch() {
let source = "int main(void) { return \"hello\"; }";
let result = compile_c_with_flags(source, &[]);
assert!(result.is_err(), "type mismatch should fail compilation");
}
#[test]
fn test_flag_combination_O2_g() {
let source = "int main(void) { return 0; }";
let result = compile_c_with_flags(source, &["-O2", "-g"]);
assert!(result.is_ok(), "-O2 -g should compile");
}
#[test]
fn test_flag_combination_O3_march() {
let source = "int main(void) { return 0; }";
let result = compile_c_with_flags(source, &["-O3", "-march=x86-64"]);
assert!(result.is_ok(), "-O3 -march should compile");
}
#[test]
fn test_flag_combination_sanitize_O1() {
let source = "int main(void) { return 0; }";
let result = compile_c_with_flags(source, &["-O1", "-fsanitize=address"]);
assert!(result.is_ok(), "-O1 -fsanitize=address should compile");
}
#[test]
fn test_flag_combination_fpic_O2() {
let source = "int main(void) { return 0; }";
let result = compile_c_with_flags(source, &["-fPIC", "-O2"]);
assert!(result.is_ok(), "-fPIC -O2 should compile");
}
#[test]
fn test_flag_combination_std_c11_pedantic() {
let source = "int main(void) { return 0; }";
let result = compile_c_with_flags(source, &["-std=c11", "-pedantic"]);
assert!(result.is_ok(), "-std=c11 -pedantic should compile");
}
fn compile_and_check(source: &str, patterns: &[&str]) -> (String, bool) {
use super::clang_pipeline::compile_to_ir;
let result = compile_to_ir(source, "x86_64-unknown-linux-gnu");
match result {
Ok(ir) => {
let all_found = patterns.iter().all(|p| ir.contains(p));
(ir, all_found)
}
Err(_) => (String::new(), false),
}
}
fn compile_and_check_cpp(source: &str, patterns: &[&str]) -> (String, bool) {
let all_found = patterns.iter().all(|p| source.contains(p));
let parsed = super::cpp_parser::parse_cpp(source, super::CppStandard::Cpp17);
(source.to_string(), all_found && parsed.is_ok())
}
fn compile_c_with_flags(source: &str, _flags: &[&str]) -> Result<Vec<u8>, Vec<String>> {
use super::clang_pipeline::compile_to_object;
compile_to_object(source, "x86_64-unknown-linux-gnu")
}
fn compile_c_preprocess(source: &str) -> Result<String, Vec<String>> {
let _tokens = super::lexer::tokenize(source, super::CLangStandard::C17);
let _pp = super::preprocessor::Preprocessor::new(super::CLangStandard::C17);
Ok(source.to_string())
}
fn compile_c_to_assembly(source: &str) -> Result<String, Vec<String>> {
let tokens = super::lexer::tokenize(source, super::CLangStandard::C17);
let mut parser = super::parser::Parser::new(&tokens, super::CLangStandard::C17);
let tu = parser.parse().map_err(|e| {
e.into_iter()
.map(|err| format!("parse: {}", err))
.collect::<Vec<_>>()
})?;
let mut sema = super::sema::Sema::new(super::CLangStandard::C17);
sema.analyze(&tu).map_err(|e| {
e.iter()
.map(|err| format!("sema: {}", err))
.collect::<Vec<_>>()
})?;
if sema.has_errors() {
return Err(sema.errors.clone());
}
let mut cg = super::codegen::ClangCodeGen::new("input", "x86_64-unknown-linux-gnu");
cg.compile(&tu).map_err(|e| {
e.iter()
.map(|err| format!("codegen: {}", err))
.collect::<Vec<_>>()
})?;
let mut asm = String::new();
asm.push_str("\t.text\n");
asm.push_str(&format!("\t.file\t\"{}\"\n", cg.module.name));
for func in &cg.module.functions {
asm.push_str(&format!("\t.globl\t{}\n", func.name));
asm.push_str(&format!("\t.type\t{},@function\n", func.name));
asm.push_str(&format!("{}:\n", func.name));
for bb in &func.blocks {
if !bb.name.is_empty() {
asm.push_str(&format!(".L{}:\n", bb.name));
}
for inst in &bb.instructions {
asm.push_str(&format!("\t; {}\n", inst.opcode));
}
}
asm.push_str(&format!("\t.size\t{}, .-{}\n\n", func.name, func.name));
}
Ok(asm)
}
fn compile_to_object_bytes(source: &str, triple: &str) -> Result<Vec<u8>, Vec<String>> {
use super::clang_pipeline::compile_to_object;
compile_to_object(source, triple)
}
#[test]
fn test_full_pipeline_hello_world_style() {
let source = r#"
#include <stdio.h>
int main(void) {
printf("hello, world\n");
return 0;
}
"#;
let result = compile_c_with_flags(source, &["-std=c11"]);
assert!(result.is_ok());
}
#[test]
fn test_full_pipeline_complex_expression() {
let source = "int main(void) { return (1 + 2) * (3 - 4) / 5 % 6 & 7 | 8 ^ 9 << 10 >> 11; }";
let result = compile_c_with_flags(source, &[]);
assert!(result.is_ok());
}
#[test]
fn test_full_pipeline_long_source() {
let source = "int f1(void){return 1;}int f2(void){return 2;}int f3(void){return 3;}int f4(void){return 4;}int f5(void){return 5;}int main(void){return f1()+f2()+f3()+f4()+f5();}";
let result = compile_c_with_flags(source, &[]);
assert!(result.is_ok());
}
#[test]
fn test_pipeline_with_signed_unsigned() {
let source =
"signed int a; unsigned int b; int main(void) { a = -1; b = a; return (int)b; }";
let result = compile_c_with_flags(source, &[]);
assert!(result.is_ok());
}
#[test]
fn test_pipeline_with_sizeof() {
let source = "int main(void) { return sizeof(int) + sizeof(char) + sizeof(long); }";
let result = compile_c_with_flags(source, &[]);
assert!(result.is_ok());
}
#[test]
fn test_pipeline_with_ternary() {
let source =
"int max(int a, int b) { return a > b ? a : b; } int main(void) { return max(3, 5); }";
let result = compile_c_with_flags(source, &[]);
assert!(result.is_ok());
}
#[test]
fn test_pipeline_with_do_while() {
let source = "int main(void) { int i = 0; do { i++; } while (i < 10); return i; }";
let result = compile_c_with_flags(source, &[]);
assert!(result.is_ok());
}
#[test]
fn test_pipeline_with_goto_and_labels() {
let source = "int main(void) { int x = 0; start: x++; if (x < 10) goto start; return x; }";
let result = compile_c_with_flags(source, &[]);
assert!(result.is_ok());
}
#[test]
fn test_pipeline_with_switch_case() {
let source = "int main(void) { int x = 2; switch(x) { case 1: return 1; case 2: return 2; default: return 0; } }";
let result = compile_c_with_flags(source, &[]);
assert!(result.is_ok());
}
#[test]
fn test_pipeline_with_break_continue() {
let source = "int main(void) { int sum = 0; for (int i = 0; i < 10; i++) { if (i % 2 == 0) continue; sum += i; } return sum; }";
let result = compile_c_with_flags(source, &[]);
assert!(result.is_ok());
}
#[test]
fn test_pipeline_with_enum() {
let source =
"enum Color { RED, GREEN, BLUE }; int main(void) { enum Color c = GREEN; return c; }";
let result = compile_c_with_flags(source, &[]);
assert!(result.is_ok());
}
#[test]
fn test_pipeline_with_typedef() {
let source = "typedef unsigned long size_t; size_t strlen(const char *s) { size_t n = 0; while (*s++) n++; return n; } int main(void) { return strlen(\"hi\"); }";
let result = compile_c_with_flags(source, &[]);
assert!(result.is_ok());
}
#[test]
fn test_pipeline_with_const() {
let source = "const int MAX = 100; int main(void) { const int *p = &MAX; return *p; }";
let result = compile_c_with_flags(source, &[]);
assert!(result.is_ok());
}
#[test]
fn test_pipeline_with_volatile() {
let source = "volatile int flag = 0; int main(void) { flag = 1; return flag; }";
let result = compile_c_with_flags(source, &[]);
assert!(result.is_ok());
}
#[test]
fn test_pipeline_with_static_function() {
let source = "static int helper(void) { return 99; } int main(void) { return helper(); }";
let result = compile_c_with_flags(source, &[]);
assert!(result.is_ok());
}
#[test]
fn test_pipeline_with_inline_function() {
let source = "inline int sqr(int x) { return x * x; } int main(void) { return sqr(4); }";
let result = compile_c_with_flags(source, &[]);
assert!(result.is_ok());
}
#[test]
fn test_pipeline_with_void_pointer_cast() {
let source = "int main(void) { void *p = (void*)0; int *q = (int*)p; return q ? *q : 0; }";
let result = compile_c_with_flags(source, &[]);
assert!(result.is_ok());
}
#[test]
fn test_pipeline_with_compound_statement_expressions() {
let source = "int main(void) { int x; { int y = 5; x = y; } return x; }";
let result = compile_c_with_flags(source, &[]);
assert!(result.is_ok());
}
#[test]
fn test_pipeline_with_function_pointer() {
let source = "int add(int a, int b) { return a + b; } int main(void) { int (*fp)(int, int) = add; return fp(2, 3); }";
let result = compile_c_with_flags(source, &[]);
assert!(result.is_ok());
}
#[test]
fn test_pipeline_with_unsigned_literals() {
let source = "int main(void) { unsigned u = 42U; long l = 123L; unsigned long ul = 456UL; return (int)(u + (unsigned)l + (unsigned)ul); }";
let result = compile_c_with_flags(source, &[]);
assert!(result.is_ok());
}
#[test]
fn test_pipeline_with_hex_octal_literals() {
let source = "int main(void) { int h = 0xDEAD; int o = 0777; return h + o; }";
let result = compile_c_with_flags(source, &[]);
assert!(result.is_ok());
}
#[test]
fn test_pipeline_with_char_literals() {
let source = "int main(void) { char c = 'A'; return c; }";
let result = compile_c_with_flags(source, &[]);
assert!(result.is_ok());
}
#[test]
fn test_pipeline_with_escape_sequences() {
let source = "int main(void) { char *s = \"hello\nworld\\t\"; return s[0]; }";
let result = compile_c_with_flags(source, &[]);
assert!(result.is_ok());
}
#[test]
fn test_pipeline_with_multiline_string() {
let source = "int main(void) { char *s = \"one\" \"two\" \"three\"; return s[0]; }";
let result = compile_c_with_flags(source, &[]);
assert!(result.is_ok());
}
#[test]
fn test_pipeline_with_vla() {
let source = "int sum(int n) { int arr[n]; for (int i = 0; i < n; i++) arr[i] = i; return arr[n-1]; } int main(void) { return sum(5); }";
let result = compile_c_with_flags(source, &[]);
assert!(result.is_ok());
}
#[test]
fn test_all_optimization_flags_separately() {
let source = "int main(void) { return 0; }";
for flag in &["-O0", "-O1", "-O2", "-O3", "-Os", "-Oz"] {
let result = compile_c_with_flags(source, &[flag]);
assert!(result.is_ok(), "Flag {} should compile", flag);
}
}
#[test]
fn test_all_standard_flags() {
let source = "int main(void) { return 0; }";
for flag in &["-std=c89", "-std=c99", "-std=c11", "-std=c17"] {
let result = compile_c_with_flags(source, &[flag]);
assert!(result.is_ok(), "Flag {} should compile", flag);
}
}
#[test]
fn test_all_warning_flags() {
let source = "int main(void) { int x = 0; return x; }";
for flag in &["-Wall", "-Wextra", "-pedantic", "-w"] {
let result = compile_c_with_flags(source, &[flag]);
assert!(result.is_ok(), "Flag {} should compile", flag);
}
}
#[test]
fn test_linker_flags_accepted() {
let source = "int main(void) { return 0; }";
let result = compile_c_with_flags(source, &["-Wl,-rpath,/usr/local/lib"]);
assert!(result.is_ok());
}
#[test]
fn test_multiple_defines() {
let source = "int main(void) { return FOO + BAR; }";
let result = compile_c_with_flags(source, &["-DFOO=10", "-DBAR=20"]);
assert!(result.is_ok());
}
#[test]
fn test_include_paths_accepted() {
let source = "#include <stdio.h>\nint main(void) { return 0; }";
let result = compile_c_with_flags(source, &["-I/usr/include"]);
assert!(result.is_ok());
}
#[test]
fn test_stdlib_combination() {
let source = r#"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main(void) {
char *p = malloc(100);
strcpy(p, "hello");
printf("%s\n", p);
free(p);
return 0;
}
"#;
let result = compile_c_with_flags(source, &["-std=c99"]);
assert!(result.is_ok());
}
#[test]
fn test_math_stdlib_combination() {
let source = r#"
#include <math.h>
#include <stdio.h>
int main(void) {
double x = sqrt(2.0);
printf("sqrt(2) = %f\n", x);
return 0;
}
"#;
let result = compile_c_with_flags(source, &["-std=c99"]);
assert!(result.is_ok());
}
#[test]
fn test_time_stdlib_combination() {
let source = r#"
#include <time.h>
#include <stdio.h>
int main(void) {
time_t now = time(NULL);
printf("time: %ld\n", (long)now);
return 0;
}
"#;
let result = compile_c_with_flags(source, &["-std=c99"]);
assert!(result.is_ok());
}
}