use crate::clang::clang_x86_pipeline::{
compile_to_x86_assembly, compile_to_x86_ir, compile_with_options, PipelineResult,
X86CompileOptions, X86IRGenerator, X86OptLevel, X86Pipeline,
};
use crate::clang::{
compile_c, compile_c_string, CLangStandard, ClangCodeGen, ClangDriver, ClangOptions,
ClangTargetInfo,
};
use crate::x86::{
x86_calling_convention::{X86ArgClass, X86CallFrame, X86CallingConvention},
x86_frame_lowering::{CallConv, X86FrameLowering},
x86_instr_info::{X86InstrInfo, X86Opcode},
x86_register_info::{X86RegisterInfo, X86_32_REG_COUNT, X86_64_REG_COUNT},
x86_subtarget::X86Subtarget,
x86_target_machine::X86TargetMachine,
};
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
#[derive(Debug, Clone)]
pub struct X86IRGenTest {
pub name: String,
pub category: X86IRGenCategory,
pub source: String,
pub target_triple: String,
pub flags: Vec<String>,
pub opt_level: X86OptLevel,
pub required_ir: Vec<X86IRPattern>,
pub forbidden_ir: Vec<X86IRPattern>,
pub should_compile: bool,
pub is_xfail: bool,
pub xfail_reason: Option<String>,
pub expected_return: Option<i32>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum X86IRGenCategory {
Types,
Constants,
Instructions,
Arithmetic,
MemoryAccess,
ControlFlow,
FunctionAttributes,
ParameterAttributes,
Linkage,
Visibility,
CallingConvention,
CLanguage,
CPlusPlus,
X86Specific,
Vectors,
InlineAsm,
ExceptionHandling,
Atomics,
Integration,
DebugInfo,
Optimization,
Stress,
}
impl X86IRGenCategory {
pub fn as_str(&self) -> &'static str {
match self {
Self::Types => "types",
Self::Constants => "constants",
Self::Instructions => "instructions",
Self::Arithmetic => "arithmetic",
Self::MemoryAccess => "memory",
Self::ControlFlow => "control_flow",
Self::FunctionAttributes => "fn_attrs",
Self::ParameterAttributes => "param_attrs",
Self::Linkage => "linkage",
Self::Visibility => "visibility",
Self::CallingConvention => "calling_conv",
Self::CLanguage => "c_lang",
Self::CPlusPlus => "cxx",
Self::X86Specific => "x86",
Self::Vectors => "vectors",
Self::InlineAsm => "inline_asm",
Self::ExceptionHandling => "eh",
Self::Atomics => "atomics",
Self::Integration => "integration",
Self::DebugInfo => "debug_info",
Self::Optimization => "optimization",
Self::Stress => "stress",
}
}
}
#[derive(Debug, Clone)]
pub struct X86IRPattern {
pub description: String,
pub pattern: String,
pub min_count: usize,
pub exact_count: Option<usize>,
pub case_sensitive: bool,
pub sub_category: String,
}
impl X86IRPattern {
pub fn required(desc: &str, pat: &str) -> Self {
Self {
description: desc.to_string(),
pattern: pat.to_string(),
min_count: 1,
exact_count: None,
case_sensitive: false,
sub_category: String::new(),
}
}
pub fn exact(desc: &str, pat: &str, count: usize) -> Self {
Self {
description: desc.to_string(),
pattern: pat.to_string(),
min_count: 0,
exact_count: Some(count),
case_sensitive: false,
sub_category: String::new(),
}
}
pub fn at_least(desc: &str, pat: &str, n: usize) -> Self {
Self {
description: desc.to_string(),
pattern: pat.to_string(),
min_count: n,
exact_count: None,
case_sensitive: false,
sub_category: String::new(),
}
}
pub fn forbidden(desc: &str, pat: &str) -> Self {
Self {
description: desc.to_string(),
pattern: pat.to_string(),
min_count: 0,
exact_count: Some(0),
case_sensitive: false,
sub_category: String::new(),
}
}
pub fn count_in(&self, text: &str) -> usize {
if self.case_sensitive {
text.matches(&self.pattern).count()
} else {
text.to_lowercase()
.matches(&self.pattern.to_lowercase())
.count()
}
}
pub fn check(&self, text: &str) -> bool {
let count = self.count_in(text);
if let Some(exact) = self.exact_count {
count == exact
} else {
count >= self.min_count
}
}
}
impl X86IRGenTest {
pub fn new(name: &str, category: X86IRGenCategory, source: &str) -> Self {
Self {
name: name.to_string(),
category,
source: source.to_string(),
target_triple: "x86_64-unknown-linux-gnu".to_string(),
flags: Vec::new(),
opt_level: X86OptLevel::O0,
required_ir: Vec::new(),
forbidden_ir: Vec::new(),
should_compile: true,
is_xfail: false,
xfail_reason: None,
expected_return: None,
}
}
pub fn with_target(mut self, triple: &str) -> Self {
self.target_triple = triple.to_string();
self
}
pub fn with_flags(mut self, flags: Vec<&str>) -> Self {
self.flags = flags.into_iter().map(String::from).collect();
self
}
pub fn with_opt(mut self, level: X86OptLevel) -> Self {
self.opt_level = level;
self
}
pub fn require(mut self, desc: &str, pat: &str) -> Self {
self.required_ir.push(X86IRPattern::required(desc, pat));
self
}
pub fn require_exact(mut self, desc: &str, pat: &str, count: usize) -> Self {
self.required_ir.push(X86IRPattern::exact(desc, pat, count));
self
}
pub fn require_at_least(mut self, desc: &str, pat: &str, n: usize) -> Self {
self.required_ir.push(X86IRPattern::at_least(desc, pat, n));
self
}
pub fn forbid(mut self, desc: &str, pat: &str) -> Self {
self.forbidden_ir.push(X86IRPattern::forbidden(desc, pat));
self
}
pub fn xfail(mut self, reason: &str) -> Self {
self.is_xfail = true;
self.xfail_reason = Some(reason.to_string());
self
}
pub fn with_expected_return(mut self, val: i32) -> Self {
self.expected_return = Some(val);
self
}
pub fn expect_compile_failure(mut self) -> Self {
self.should_compile = false;
self
}
}
#[derive(Debug, Clone)]
pub struct X86IRGenTestResult {
pub name: String,
pub passed: bool,
pub ir_text: Option<String>,
pub check_results: Vec<X86IRCheckResult>,
pub compiled: bool,
pub compile_error: Option<String>,
pub duration_ms: u64,
}
#[derive(Debug, Clone)]
pub struct X86IRCheckResult {
pub description: String,
pub passed: bool,
pub count: usize,
pub min_count: usize,
pub exact_count: Option<usize>,
pub is_forbidden: bool,
}
impl X86IRGenTestResult {
pub fn from_test(
test: &X86IRGenTest,
ir_text: &str,
compiled: bool,
compile_error: Option<&str>,
duration_ms: u64,
) -> Self {
let mut check_results = Vec::new();
for pat in &test.required_ir {
let count = pat.count_in(ir_text);
let passed = pat.check(ir_text);
check_results.push(X86IRCheckResult {
description: pat.description.clone(),
passed,
count,
min_count: pat.min_count,
exact_count: pat.exact_count,
is_forbidden: false,
});
}
for pat in &test.forbidden_ir {
let count = pat.count_in(ir_text);
let passed = count == 0;
check_results.push(X86IRCheckResult {
description: format!("[FORBIDDEN] {}", pat.description),
passed,
count,
min_count: 0,
exact_count: Some(0),
is_forbidden: true,
});
}
let passed = check_results.iter().all(|r| r.passed);
Self {
name: test.name.clone(),
passed,
ir_text: Some(ir_text.to_string()),
check_results,
compiled,
compile_error: compile_error.map(String::from),
duration_ms,
}
}
pub fn compile_failure(test: &X86IRGenTest, error: &str, duration_ms: u64) -> Self {
Self {
name: test.name.clone(),
passed: !test.should_compile,
ir_text: None,
check_results: Vec::new(),
compiled: false,
compile_error: Some(error.to_string()),
duration_ms,
}
}
pub fn summary(&self) -> String {
let status = if self.passed { "PASS" } else { "FAIL" };
let count = self.check_results.len();
format!("{} — {} ({} checks)", status, self.name, count)
}
pub fn failed_checks(&self) -> Vec<&str> {
self.check_results
.iter()
.filter(|r| !r.passed)
.map(|r| r.description.as_str())
.collect()
}
pub fn failed_count(&self) -> usize {
self.check_results.iter().filter(|r| !r.passed).count()
}
}
#[derive(Debug, Clone)]
pub struct X86IRGenTestSuite {
pub name: String,
pub description: String,
pub tests: Vec<X86IRGenTest>,
}
impl X86IRGenTestSuite {
pub fn new(name: &str, desc: &str) -> Self {
Self {
name: name.to_string(),
description: desc.to_string(),
tests: Vec::new(),
}
}
pub fn add(&mut self, test: X86IRGenTest) {
self.tests.push(test);
}
pub fn add_many(&mut self, tests: Vec<X86IRGenTest>) {
self.tests.extend(tests);
}
pub fn len(&self) -> usize {
self.tests.len()
}
pub fn is_empty(&self) -> bool {
self.tests.is_empty()
}
pub fn filter_by_category(&self, cat: X86IRGenCategory) -> Vec<&X86IRGenTest> {
self.tests.iter().filter(|t| t.category == cat).collect()
}
pub fn filter_by_name(&self, substr: &str) -> Vec<&X86IRGenTest> {
self.tests
.iter()
.filter(|t| t.name.contains(substr))
.collect()
}
}
#[derive(Debug, Clone)]
pub struct TypeIRPattern {
pub c_type: String,
pub ir_type: String,
pub category: String,
}
impl TypeIRPattern {
pub fn new(c_type: &str, ir_type: &str, category: &str) -> Self {
Self {
c_type: c_type.to_string(),
ir_type: ir_type.to_string(),
category: category.to_string(),
}
}
}
pub fn x86_type_map() -> Vec<TypeIRPattern> {
vec![
TypeIRPattern::new("void", "void", "scalar"),
TypeIRPattern::new("_Bool", "i1", "scalar"),
TypeIRPattern::new("char", "i8", "scalar"),
TypeIRPattern::new("signed char", "i8", "scalar"),
TypeIRPattern::new("unsigned char", "i8", "scalar"),
TypeIRPattern::new("short", "i16", "scalar"),
TypeIRPattern::new("unsigned short", "i16", "scalar"),
TypeIRPattern::new("int", "i32", "scalar"),
TypeIRPattern::new("unsigned int", "i32", "scalar"),
TypeIRPattern::new("long", "i64", "scalar"),
TypeIRPattern::new("unsigned long", "i64", "scalar"),
TypeIRPattern::new("long long", "i64", "scalar"),
TypeIRPattern::new("unsigned long long", "i64", "scalar"),
TypeIRPattern::new("float", "float", "scalar"),
TypeIRPattern::new("double", "double", "scalar"),
TypeIRPattern::new("long double", "x86_fp80", "scalar"),
TypeIRPattern::new("__fp16 / _Float16", "half", "scalar"),
TypeIRPattern::new("__float128", "fp128", "scalar"),
TypeIRPattern::new("int *", "ptr", "pointer"),
TypeIRPattern::new("void *", "ptr", "pointer"),
TypeIRPattern::new("double *", "ptr", "pointer"),
TypeIRPattern::new("struct S *", "ptr", "pointer"),
TypeIRPattern::new("int[10]", "[10 x i32]", "array"),
TypeIRPattern::new("struct { int; double; }", "{ i32, double }", "aggregate"),
TypeIRPattern::new("v4si (GCC vector)", "<4 x i32>", "vector"),
]
}
pub fn build_type_tests() -> Vec<X86IRGenTest> {
vec![
X86IRGenTest::new("ir_type_void_return", X86IRGenCategory::Types,
"void f(void) {}")
.require("void return type in IR", "void")
.require("ret void instruction", "ret void"),
X86IRGenTest::new("ir_type_void_ptr", X86IRGenCategory::Types,
"void f(void *p) { (void)p; }")
.require("void pointer param", "void\\*")
.require("alloca for pointer", "alloca"),
X86IRGenTest::new("ir_type_i1_bool", X86IRGenCategory::Types,
"_Bool f(_Bool x) { return x; }")
.require("i1 type for _Bool", "i1"),
X86IRGenTest::new("ir_type_i1_cmp", X86IRGenCategory::Types,
"_Bool f(int a, int b) { return a < b; }")
.require("icmp produces i1", "icmp")
.require("i1 result", "i1"),
X86IRGenTest::new("ir_type_i8_char", X86IRGenCategory::Types,
"char f(char c) { return c; }")
.require("i8 type for char", "i8"),
X86IRGenTest::new("ir_type_i8_signed_char", X86IRGenCategory::Types,
"signed char f(signed char c) { return c; }")
.require("i8 type for signed char", "i8"),
X86IRGenTest::new("ir_type_i8_unsigned_char", X86IRGenCategory::Types,
"unsigned char f(unsigned char c) { return c; }")
.require("i8 type for unsigned char", "i8"),
X86IRGenTest::new("ir_type_i16_short", X86IRGenCategory::Types,
"short f(short s) { return s; }")
.require("i16 type for short", "i16"),
X86IRGenTest::new("ir_type_i16_ushort", X86IRGenCategory::Types,
"unsigned short f(unsigned short s) { return s; }")
.require("i16 type for unsigned short", "i16"),
X86IRGenTest::new("ir_type_i32_int", X86IRGenCategory::Types,
"int f(int x) { return x; }")
.require("i32 type for int", "i32"),
X86IRGenTest::new("ir_type_i32_unsigned", X86IRGenCategory::Types,
"unsigned f(unsigned x) { return x; }")
.require("i32 type for unsigned int", "i32"),
X86IRGenTest::new("ir_type_i64_long", X86IRGenCategory::Types,
"long f(long x) { return x; }")
.with_target("x86_64-unknown-linux-gnu")
.require("i64 type for long on LP64", "i64"),
X86IRGenTest::new("ir_type_i64_long_long", X86IRGenCategory::Types,
"long long f(long long x) { return x; }")
.require("i64 type for long long", "i64"),
X86IRGenTest::new("ir_type_i64_ull", X86IRGenCategory::Types,
"unsigned long long f(unsigned long long x) { return x; }")
.require("i64 type for unsigned long long", "i64"),
X86IRGenTest::new("ir_type_half", X86IRGenCategory::Types,
"_Float16 f(_Float16 x) { return x; }")
.with_flags(vec!["-std=c23"])
.require("half type for _Float16", "half"),
X86IRGenTest::new("ir_type_float", X86IRGenCategory::Types,
"float f(float x) { return x; }")
.require("float type for float", "float"),
X86IRGenTest::new("ir_type_float_literal", X86IRGenCategory::Types,
"float f(void) { return 3.14f; }")
.require("float literal constant", "float"),
X86IRGenTest::new("ir_type_double", X86IRGenCategory::Types,
"double f(double x) { return x; }")
.require("double type for double", "double"),
X86IRGenTest::new("ir_type_double_literal", X86IRGenCategory::Types,
"double f(void) { return 2.718281828; }")
.require("double literal constant", "double"),
X86IRGenTest::new("ir_type_x86_fp80", X86IRGenCategory::Types,
"long double f(long double x) { return x; }")
.require("x86_fp80 type for long double", "x86_fp80"),
X86IRGenTest::new("ir_type_fp128", X86IRGenCategory::Types,
"__float128 f(__float128 x) { return x; }")
.with_flags(vec!["-std=gnu23"])
.require("fp128 type for __float128", "fp128"),
X86IRGenTest::new("ir_type_ptr_int_ptr", X86IRGenCategory::Types,
"int f(int *p) { return *p; }")
.require("pointer to int", "i32\\*")
.require("load from pointer", "load"),
X86IRGenTest::new("ir_type_ptr_double_ptr", X86IRGenCategory::Types,
"double f(double *p) { return *p; }")
.require("pointer to double in load", "double\\*")
.require("floating-point load", "load double"),
X86IRGenTest::new("ir_type_ptr_null", X86IRGenCategory::Types,
"int *f(void) { return 0; }")
.require("null pointer constant", "null"),
X86IRGenTest::new("ir_type_ptr_void_ptr", X86IRGenCategory::Types,
"void *f(int *p) { return (void *)p; }")
.require("pointer cast to void*", "i8\\*"),
X86IRGenTest::new("ir_type_array_fixed", X86IRGenCategory::Types,
"int f(void) { int a[10]; return a[0]; }")
.require("fixed-size array alloca", "alloca")
.require("array type in IR", "i32"),
X86IRGenTest::new("ir_type_array_multi_dim", X86IRGenCategory::Types,
"int f(void) { int a[3][4]; return a[0][0]; }")
.require("multi-dim array GEP", "getelementptr"),
X86IRGenTest::new("ir_type_struct_simple", X86IRGenCategory::Types,
"struct S { int a; double b; }; double f(struct S s) { return s.b; }")
.require("struct type in IR", "type")
.require("struct field access via GEP", "getelementptr"),
X86IRGenTest::new("ir_type_struct_nested", X86IRGenCategory::Types,
"struct A { int x; }; struct B { struct A a; int y; }; int f(struct B b) { return b.a.x; }")
.require("nested struct GEP", "getelementptr"),
X86IRGenTest::new("ir_type_struct_return", X86IRGenCategory::Types,
"struct S { int a; int b; }; struct S f(void) { struct S s = {1,2}; return s; }")
.require("struct type definition", "type"),
X86IRGenTest::new("ir_type_vector_v4i32", X86IRGenCategory::Types,
"typedef int v4si __attribute__((vector_size(16))); v4si f(v4si a, v4si b) { return a + b; }")
.require("vector type <4 x i32>", "<4 x i32>")
.require("vector add instruction", "add <4 x i32>"),
X86IRGenTest::new("ir_type_vector_v2f64", X86IRGenCategory::Types,
"typedef double v2df __attribute__((vector_size(16))); v2df f(v2df a, v2df b) { return a + b; }")
.require("vector type <2 x double>", "<2 x double>")
.require("vector fadd", "fadd <2 x double>"),
X86IRGenTest::new("ir_type_vector_v8i16", X86IRGenCategory::Types,
"typedef short v8hi __attribute__((vector_size(16))); v8hi f(v8hi a, v8hi b) { return a + b; }")
.require("vector type <8 x i16>", "<8 x i16>"),
X86IRGenTest::new("ir_type_vector_v16i8", X86IRGenCategory::Types,
"typedef char v16qi __attribute__((vector_size(16))); v16qi f(v16qi a, v16qi b) { return a + b; }")
.require("vector type <16 x i8>", "<16 x i8>"),
X86IRGenTest::new("ir_type_vector_v4f32", X86IRGenCategory::Types,
"typedef float v4sf __attribute__((vector_size(16))); v4sf f(v4sf a, v4sf b) { return a + b; }")
.require("vector type <4 x float>", "<4 x float>"),
X86IRGenTest::new("ir_type_vector_v2i64", X86IRGenCategory::Types,
"typedef long long v2di __attribute__((vector_size(16))); v2di f(v2di a, v2di b) { return a + b; }")
.require("vector type <2 x i64>", "<2 x i64>"),
X86IRGenTest::new("ir_type_target_triple", X86IRGenCategory::Types,
"int f(int x) { return x; }")
.with_target("x86_64-unknown-linux-gnu")
.require("target triple present", "target triple"),
X86IRGenTest::new("ir_type_wchar_t", X86IRGenCategory::Types,
"#include <stddef.h>\nwchar_t f(wchar_t c) { return c; }")
.require("wchar_t type lowering", "i32"),
X86IRGenTest::new("ir_type_int8_t", X86IRGenCategory::Types,
"#include <stdint.h>\nint8_t f(int8_t x) { return x; }")
.require("int8_t maps to i8", "i8"),
X86IRGenTest::new("ir_type_uint8_t", X86IRGenCategory::Types,
"#include <stdint.h>\nuint8_t f(uint8_t x) { return x; }")
.require("uint8_t maps to i8", "i8"),
X86IRGenTest::new("ir_type_int16_t", X86IRGenCategory::Types,
"#include <stdint.h>\nint16_t f(int16_t x) { return x; }")
.require("int16_t maps to i16", "i16"),
X86IRGenTest::new("ir_type_uint16_t", X86IRGenCategory::Types,
"#include <stdint.h>\nuint16_t f(uint16_t x) { return x; }")
.require("uint16_t maps to i16", "i16"),
X86IRGenTest::new("ir_type_int32_t", X86IRGenCategory::Types,
"#include <stdint.h>\nint32_t f(int32_t x) { return x; }")
.require("int32_t maps to i32", "i32"),
X86IRGenTest::new("ir_type_uint32_t", X86IRGenCategory::Types,
"#include <stdint.h>\nuint32_t f(uint32_t x) { return x; }")
.require("uint32_t maps to i32", "i32"),
X86IRGenTest::new("ir_type_int64_t", X86IRGenCategory::Types,
"#include <stdint.h>\nint64_t f(int64_t x) { return x; }")
.require("int64_t maps to i64", "i64"),
X86IRGenTest::new("ir_type_uint64_t", X86IRGenCategory::Types,
"#include <stdint.h>\nuint64_t f(uint64_t x) { return x; }")
.require("uint64_t maps to i64", "i64"),
X86IRGenTest::new("ir_type_intptr_t", X86IRGenCategory::Types,
"#include <stdint.h>\nintptr_t f(intptr_t x) { return x; }")
.require("intptr_t lowering", "i64"),
X86IRGenTest::new("ir_type_uintptr_t", X86IRGenCategory::Types,
"#include <stdint.h>\nuintptr_t f(uintptr_t x) { return x; }")
.require("uintptr_t lowering", "i64"),
X86IRGenTest::new("ir_type_ptrdiff_t", X86IRGenCategory::Types,
"#include <stddef.h>\nptrdiff_t f(ptrdiff_t x) { return x; }")
.require("ptrdiff_t lowering", "i64"),
X86IRGenTest::new("ir_type_size_t", X86IRGenCategory::Types,
"#include <stddef.h>\nsize_t f(size_t x) { return x; }")
.require("size_t lowering", "i64"),
X86IRGenTest::new("ir_type_ssize_t", X86IRGenCategory::Types,
"#include <sys/types.h>\nssize_t f(ssize_t x) { return x; }")
.require("ssize_t lowering", "i64"),
X86IRGenTest::new("ir_type_enum", X86IRGenCategory::Types,
"enum E { A, B, C }; enum E f(enum E e) { return e; }")
.require("enum type lowers to i32", "i32"),
X86IRGenTest::new("ir_type_enum_specified", X86IRGenCategory::Types,
"enum E : char { A, B }; enum E f(enum E e) { return e; }")
.require("fixed-type enum lowers to i8", "i8"),
X86IRGenTest::new("ir_type_union", X86IRGenCategory::Types,
"union U { int i; float f; }; int f(union U u) { return u.i; }")
.require("union type in IR", "type"),
X86IRGenTest::new("ir_type_bitfield", X86IRGenCategory::Types,
"struct S { int a : 3; int b : 5; }; int f(struct S s) { return s.a; }")
.require("struct with bitfields", "type"),
X86IRGenTest::new("ir_type_complex_float", X86IRGenCategory::Types,
"#include <complex.h>\nfloat _Complex f(float _Complex z) { return z; }")
.require("complex float type", "float"),
X86IRGenTest::new("ir_type_complex_double", X86IRGenCategory::Types,
"#include <complex.h>\ndouble _Complex f(double _Complex z) { return z; }")
.require("complex double type", "double"),
X86IRGenTest::new("ir_type_int128", X86IRGenCategory::Types,
"__int128 f(__int128 x) { return x; }")
.with_flags(vec!["-std=gnu17"])
.require("i128 type for __int128", "i128"),
X86IRGenTest::new("ir_type_nested_array", X86IRGenCategory::Types,
"int f(void) { int a[2][3][4]; return a[0][1][2]; }")
.require("nested array GEP", "getelementptr"),
X86IRGenTest::new("ir_type_incomplete_struct", X86IRGenCategory::Types,
"struct S; struct S *f(struct S *p) { return p; }")
.require("opaque struct pointer", "%struct.S"),
X86IRGenTest::new("ir_type_typedef_int", X86IRGenCategory::Types,
"typedef int myint; myint f(myint x) { return x; }")
.require("typedef resolves to i32", "i32"),
X86IRGenTest::new("ir_type_func_ptr_simple", X86IRGenCategory::Types,
"typedef int (*fnptr)(int); int f(fnptr fp, int x) { return fp(x); }")
.require("function pointer type", "ptr"),
X86IRGenTest::new("ir_type_array_func_ptr", X86IRGenCategory::Types,
"int f(int (*arr[4])(int), int x, int i) { return arr[i](x); }")
.require("array of function pointers", "ptr"),
]
}
pub fn build_constant_tests() -> Vec<X86IRGenTest> {
vec![
X86IRGenTest::new("ir_const_int_zero", X86IRGenCategory::Constants,
"int f(void) { return 0; }")
.require("integer constant 0", "ret i32 0"),
X86IRGenTest::new("ir_const_int_positive", X86IRGenCategory::Constants,
"int f(void) { return 42; }")
.require("integer constant 42", "ret i32 42"),
X86IRGenTest::new("ir_const_int_negative", X86IRGenCategory::Constants,
"int f(void) { return -1; }")
.require("integer constant -1", "-1"),
X86IRGenTest::new("ir_const_int_max", X86IRGenCategory::Constants,
"int f(void) { return 2147483647; }")
.require("large integer constant", "2147483647"),
X86IRGenTest::new("ir_const_int_hex", X86IRGenCategory::Constants,
"int f(void) { return 0xDEADBEEF; }")
.require("hex integer constant", "3735928559"),
X86IRGenTest::new("ir_const_float_simple", X86IRGenCategory::Constants,
"float f(void) { return 1.5f; }")
.require("float constant 1.5", "float"),
X86IRGenTest::new("ir_const_double_simple", X86IRGenCategory::Constants,
"double f(void) { return 3.14159265358979; }")
.require("double constant pi", "double"),
X86IRGenTest::new("ir_const_double_zero", X86IRGenCategory::Constants,
"double f(void) { return 0.0; }")
.require("double zero constant", "double 0"),
X86IRGenTest::new("ir_const_null_ptr", X86IRGenCategory::Constants,
"int *f(void) { return (int *)0; }")
.require("null pointer constant", "null"),
X86IRGenTest::new("ir_const_null_void_ptr", X86IRGenCategory::Constants,
"void *f(void) { return 0; }")
.require("null pointer from int", "null"),
X86IRGenTest::new("ir_const_zero_initializer", X86IRGenCategory::Constants,
"struct S { int a; double b; }; struct S f(void) { struct S s = {0}; return s; }")
.require("zeroinitializer for struct", "zeroinitializer"),
X86IRGenTest::new("ir_const_gep_global", X86IRGenCategory::Constants,
"int arr[10]; int *f(void) { return &arr[5]; }")
.require("GEP expression for global array element", "getelementptr"),
X86IRGenTest::new("ir_const_bitcast_ptr", X86IRGenCategory::Constants,
"int f(void *p) { return *(int *)p; }")
.require("pointer cast to int*", "i32\\*"),
X86IRGenTest::new("ir_const_inttoptr", X86IRGenCategory::Constants,
"void *f(unsigned long x) { return (void *)x; }")
.require("inttoptr operation", "inttoptr"),
X86IRGenTest::new("ir_const_ptrtoint", X86IRGenCategory::Constants,
"unsigned long f(void *p) { return (unsigned long)p; }")
.require("ptrtoint operation", "ptrtoint"),
X86IRGenTest::new("ir_const_undef_uninit", X86IRGenCategory::Constants,
"int f(void) { int x; return x; }")
.with_flags(vec!["-Wno-uninitialized"])
.require("uninitialized variable", "alloca"),
X86IRGenTest::new("ir_const_add_fold", X86IRGenCategory::Constants,
"int f(void) { return 2 + 3; }")
.require("constant fold 2+3 to 5", "ret i32 5"),
X86IRGenTest::new("ir_const_sub_fold", X86IRGenCategory::Constants,
"int f(void) { return 10 - 3; }")
.require("constant fold 10-3 to 7", "ret i32 7"),
X86IRGenTest::new("ir_const_mul_fold", X86IRGenCategory::Constants,
"int f(void) { return 6 * 7; }")
.require("constant fold 6*7 to 42", "ret i32 42"),
X86IRGenTest::new("ir_const_div_fold", X86IRGenCategory::Constants,
"int f(void) { return 100 / 5; }")
.require("constant fold division", "ret i32 20"),
X86IRGenTest::new("ir_const_mod_fold", X86IRGenCategory::Constants,
"int f(void) { return 17 % 5; }")
.require("constant fold modulo", "ret i32 2"),
X86IRGenTest::new("ir_const_and_fold", X86IRGenCategory::Constants,
"int f(void) { return 0xFF & 0x0F; }")
.require("constant fold bitwise and", "ret i32 15"),
X86IRGenTest::new("ir_const_or_fold", X86IRGenCategory::Constants,
"int f(void) { return 0xF0 | 0x0F; }")
.require("constant fold bitwise or", "ret i32 255"),
X86IRGenTest::new("ir_const_xor_fold", X86IRGenCategory::Constants,
"int f(void) { return 0xAA ^ 0x55; }")
.require("constant fold bitwise xor", "ret i32 255"),
X86IRGenTest::new("ir_const_shl_fold", X86IRGenCategory::Constants,
"int f(void) { return 1 << 10; }")
.require("constant fold shift left", "ret i32 1024"),
X86IRGenTest::new("ir_const_lshr_fold", X86IRGenCategory::Constants,
"unsigned f(void) { return 1024u >> 3; }")
.require("constant fold logical shift right", "ret i32 128"),
X86IRGenTest::new("ir_const_compare_fold", X86IRGenCategory::Constants,
"int f(void) { return 5 > 3 ? 1 : 0; }")
.require("constant fold comparison to true", "ret i32 1"),
X86IRGenTest::new("ir_const_ternary_fold", X86IRGenCategory::Constants,
"int f(void) { return 1 ? 42 : 99; }")
.require("constant fold ternary with known condition", "ret i32 42"),
X86IRGenTest::new("ir_const_address_of_global", X86IRGenCategory::Constants,
"int x; int *f(void) { return &x; }")
.require("address-of global constant", "@x"),
X86IRGenTest::new("ir_const_sizeof_type", X86IRGenCategory::Constants,
"#include <stddef.h>\nsize_t f(void) { return sizeof(int); }")
.require("sizeof(int) constant folded to 4", "ret i64 4"),
X86IRGenTest::new("ir_const_alignof_type", X86IRGenCategory::Constants,
"#include <stddef.h>\nsize_t f(void) { return _Alignof(double); }")
.require("alignof constant expression", "ret i64"),
X86IRGenTest::new("ir_const_offsetof", X86IRGenCategory::Constants,
"#include <stddef.h>\nstruct S { int a; char b; double c; }; size_t f(void) { return offsetof(struct S, c); }")
.require("offsetof constant", "ret i64"),
X86IRGenTest::new("ir_const_bool_true", X86IRGenCategory::Constants,
"#include <stdbool.h>\nbool f(void) { return true; }")
.require("bool true constant", "true"),
X86IRGenTest::new("ir_const_bool_false", X86IRGenCategory::Constants,
"#include <stdbool.h>\nbool f(void) { return false; }")
.require("bool false constant", "false"),
X86IRGenTest::new("ir_const_struct_aggregate", X86IRGenCategory::Constants,
"struct S { int a; double b; }; struct S f(void) { return (struct S){1, 3.14}; }")
.require("aggregate constant for struct literal", "insertvalue"),
X86IRGenTest::new("ir_const_vector_splat", X86IRGenCategory::Constants,
"typedef int v4si __attribute__((vector_size(16))); v4si f(void) { return (v4si){0, 0, 0, 0}; }")
.require("zero splat vector constant", "zeroinitializer"),
]
}
pub fn build_memory_instruction_tests() -> Vec<X86IRGenTest> {
vec![
X86IRGenTest::new(
"ir_inst_alloca_scalar",
X86IRGenCategory::Instructions,
"int f(void) { int x = 42; return x; }",
)
.require("alloca for local int", "alloca i32"),
X86IRGenTest::new(
"ir_inst_alloca_array",
X86IRGenCategory::Instructions,
"int f(void) { int arr[10]; return arr[0]; }",
)
.require("alloca for array", "alloca [10 x i32]"),
X86IRGenTest::new(
"ir_inst_alloca_aligned",
X86IRGenCategory::Instructions,
"int f(void) { int x __attribute__((aligned(16))) = 0; return x; }",
)
.require("aligned alloca", "align 16"),
X86IRGenTest::new(
"ir_inst_load_int",
X86IRGenCategory::Instructions,
"int f(int *p) { return *p; }",
)
.require("load i32", "load i32"),
X86IRGenTest::new(
"ir_inst_load_float",
X86IRGenCategory::Instructions,
"float f(float *p) { return *p; }",
)
.require("load float", "load float"),
X86IRGenTest::new(
"ir_inst_load_double",
X86IRGenCategory::Instructions,
"double f(double *p) { return *p; }",
)
.require("load double", "load double"),
X86IRGenTest::new(
"ir_inst_load_i8",
X86IRGenCategory::Instructions,
"char f(char *p) { return *p; }",
)
.require("load i8", "load i8"),
X86IRGenTest::new(
"ir_inst_load_i16",
X86IRGenCategory::Instructions,
"short f(short *p) { return *p; }",
)
.require("load i16", "load i16"),
X86IRGenTest::new(
"ir_inst_load_i64",
X86IRGenCategory::Instructions,
"long long f(long long *p) { return *p; }",
)
.require("load i64", "load i64"),
X86IRGenTest::new(
"ir_inst_store_int",
X86IRGenCategory::Instructions,
"void f(int *p) { *p = 42; }",
)
.require("store i32", "store i32"),
X86IRGenTest::new(
"ir_inst_store_double",
X86IRGenCategory::Instructions,
"void f(double *p) { *p = 3.14; }",
)
.require("store double", "store double"),
X86IRGenTest::new(
"ir_inst_gep_array",
X86IRGenCategory::Instructions,
"int f(int *p, int i) { return p[i]; }",
)
.require("getelementptr for array access", "getelementptr"),
X86IRGenTest::new(
"ir_inst_gep_struct",
X86IRGenCategory::Instructions,
"struct S { int a; double b; }; double f(struct S *s) { return s->b; }",
)
.require("getelementptr for struct field", "getelementptr"),
X86IRGenTest::new(
"ir_inst_gep_struct_idx",
X86IRGenCategory::Instructions,
"struct S { int a; int b; int c; }; int f(struct S *s) { return s->c; }",
)
.require("gep i32 2 for third field", "getelementptr"),
X86IRGenTest::new(
"ir_inst_gep_inbounds",
X86IRGenCategory::Instructions,
"int f(int arr[10]) { return arr[5]; }",
)
.require("inbounds GEP", "inbounds"),
X86IRGenTest::new(
"ir_inst_phi_simple",
X86IRGenCategory::Instructions,
"int f(int x) { int y = x ? 1 : 2; return y; }",
)
.require("phi instruction for ternary", "phi i32"),
X86IRGenTest::new(
"ir_inst_select",
X86IRGenCategory::Instructions,
"int f(int a, int b, int c) { return a ? b : c; }",
)
.require("select instruction", "select"),
]
}
pub fn build_arithmetic_tests() -> Vec<X86IRGenTest> {
vec![
X86IRGenTest::new("ir_arith_add_int", X86IRGenCategory::Arithmetic,
"int f(int a, int b) { return a + b; }")
.require("add i32", "add i32"),
X86IRGenTest::new("ir_arith_add_float", X86IRGenCategory::Arithmetic,
"float f(float a, float b) { return a + b; }")
.require("fadd float", "fadd float"),
X86IRGenTest::new("ir_arith_add_double", X86IRGenCategory::Arithmetic,
"double f(double a, double b) { return a + b; }")
.require("fadd double", "fadd double"),
X86IRGenTest::new("ir_arith_sub_int", X86IRGenCategory::Arithmetic,
"int f(int a, int b) { return a - b; }")
.require("sub i32", "sub i32"),
X86IRGenTest::new("ir_arith_sub_float", X86IRGenCategory::Arithmetic,
"float f(float a, float b) { return a - b; }")
.require("fsub float", "fsub float"),
X86IRGenTest::new("ir_arith_mul_int", X86IRGenCategory::Arithmetic,
"int f(int a, int b) { return a * b; }")
.require("mul i32", "mul i32"),
X86IRGenTest::new("ir_arith_mul_float", X86IRGenCategory::Arithmetic,
"float f(float a, float b) { return a * b; }")
.require("fmul float", "fmul float"),
X86IRGenTest::new("ir_arith_sdiv_int", X86IRGenCategory::Arithmetic,
"int f(int a, int b) { return a / b; }")
.require("sdiv i32", "sdiv i32"),
X86IRGenTest::new("ir_arith_udiv_uint", X86IRGenCategory::Arithmetic,
"unsigned f(unsigned a, unsigned b) { return a / b; }")
.require("udiv i32 for unsigned", "udiv i32"),
X86IRGenTest::new("ir_arith_srem_int", X86IRGenCategory::Arithmetic,
"int f(int a, int b) { return a % b; }")
.require("srem i32", "srem i32"),
X86IRGenTest::new("ir_arith_urem_uint", X86IRGenCategory::Arithmetic,
"unsigned f(unsigned a, unsigned b) { return a % b; }")
.require("urem i32 for unsigned", "urem i32"),
X86IRGenTest::new("ir_arith_fdiv_float", X86IRGenCategory::Arithmetic,
"float f(float a, float b) { return a / b; }")
.require("fdiv float", "fdiv float"),
X86IRGenTest::new("ir_arith_fdiv_double", X86IRGenCategory::Arithmetic,
"double f(double a, double b) { return a / b; }")
.require("fdiv double", "fdiv double"),
X86IRGenTest::new("ir_arith_frem_float", X86IRGenCategory::Arithmetic,
"#include <math.h>\nfloat f(float a, float b) { return fmodf(a, b); }")
.require("frem float", "frem"),
X86IRGenTest::new("ir_arith_mixed_add_sub", X86IRGenCategory::Arithmetic,
"int f(int a, int b, int c) { return a + b - c; }")
.require("add and sub in same function", "add i32")
.require("sub after add", "sub i32"),
X86IRGenTest::new("ir_arith_mul_add_fold", X86IRGenCategory::Arithmetic,
"int f(int a) { return a * 2 + 1; }")
.require("mul by constant", "mul i32"),
X86IRGenTest::new("ir_arith_div_by_power_of_two", X86IRGenCategory::Arithmetic,
"int f(int a) { return a / 4; }")
.with_opt(X86OptLevel::O1)
.require("sdiv or ashr for power-of-two div", "sdiv"),
X86IRGenTest::new("ir_arith_rem_by_power_of_two", X86IRGenCategory::Arithmetic,
"unsigned f(unsigned a) { return a % 16; }")
.with_opt(X86OptLevel::O1)
.require("urem for unsigned mod", "urem"),
X86IRGenTest::new("ir_arith_negate_int", X86IRGenCategory::Arithmetic,
"int f(int a) { return -a; }")
.require("sub from zero for negation", "sub i32 0"),
X86IRGenTest::new("ir_arith_negate_float", X86IRGenCategory::Arithmetic,
"float f(float a) { return -a; }")
.require("fneg float", "fneg float"),
X86IRGenTest::new("ir_arith_negate_double", X86IRGenCategory::Arithmetic,
"double f(double a) { return -a; }")
.require("fneg double", "fneg double"),
X86IRGenTest::new("ir_arith_fma_pattern", X86IRGenCategory::Arithmetic,
"double f(double a, double b, double c) { return a * b + c; }")
.with_opt(X86OptLevel::O2)
.require("possibly contracted to llvm.fmuladd", "call"),
X86IRGenTest::new("ir_arith_abs_int", X86IRGenCategory::Arithmetic,
"int f(int a) { return a >= 0 ? a : -a; }")
.require("abs via select/cmp", "icmp")
.require("negate on negative branch", "sub"),
X86IRGenTest::new("ir_arith_abs_long", X86IRGenCategory::Arithmetic,
"long f(long a) { return a >= 0 ? a : -a; }")
.require("abs for i64", "i64"),
X86IRGenTest::new("ir_arith_min_int", X86IRGenCategory::Arithmetic,
"int f(int a, int b) { return a < b ? a : b; }")
.require("min via select/icmp slt", "icmp slt")
.require("select for min", "select i32"),
X86IRGenTest::new("ir_arith_max_int", X86IRGenCategory::Arithmetic,
"int f(int a, int b) { return a > b ? a : b; }")
.require("max via select/icmp sgt", "icmp sgt")
.require("select for max", "select i32"),
X86IRGenTest::new("ir_arith_overflow_add", X86IRGenCategory::Arithmetic,
"#include <stdbool.h>\nbool f(int a, int b, int *result) { return __builtin_add_overflow(a, b, result); }")
.require("add with overflow intrinsic", "llvm.sadd.with.overflow"),
X86IRGenTest::new("ir_arith_overflow_sub", X86IRGenCategory::Arithmetic,
"#include <stdbool.h>\nbool f(int a, int b, int *result) { return __builtin_sub_overflow(a, b, result); }")
.require("sub with overflow intrinsic", "llvm.ssub.with.overflow"),
X86IRGenTest::new("ir_arith_overflow_mul", X86IRGenCategory::Arithmetic,
"#include <stdbool.h>\nbool f(int a, int b, int *result) { return __builtin_mul_overflow(a, b, result); }")
.require("mul with overflow intrinsic", "llvm.smul.with.overflow"),
X86IRGenTest::new("ir_arith_saturating_add", X86IRGenCategory::Arithmetic,
"#include <stdbool.h>\nint f(int a, int b) { int r; __builtin_add_overflow(a, b, &r); return r; }")
.require("overflow intrinsic for saturating add", "llvm.sadd.with.overflow"),
X86IRGenTest::new("ir_arith_i8_arithmetic", X86IRGenCategory::Arithmetic,
"char f(char a, char b) { return a + b; }")
.require("trunc/sext pattern for i8 arithmetic", "sext")
.require("add after promotion", "add i32"),
X86IRGenTest::new("ir_arith_i16_arithmetic", X86IRGenCategory::Arithmetic,
"short f(short a, short b) { return a * b; }")
.require("sext for i16 multiplication", "sext")
.require("mul after promotion", "mul i32"),
]
}
pub fn build_bitwise_tests() -> Vec<X86IRGenTest> {
vec![
X86IRGenTest::new(
"ir_bitwise_shl",
X86IRGenCategory::Instructions,
"int f(int a, int b) { return a << b; }",
)
.require("shl i32", "shl i32"),
X86IRGenTest::new(
"ir_bitwise_lshr",
X86IRGenCategory::Instructions,
"unsigned f(unsigned a, unsigned b) { return a >> b; }",
)
.require("lshr i32", "lshr i32"),
X86IRGenTest::new(
"ir_bitwise_ashr",
X86IRGenCategory::Instructions,
"int f(int a, int b) { return a >> b; }",
)
.require("ashr i32", "ashr i32"),
X86IRGenTest::new(
"ir_bitwise_and",
X86IRGenCategory::Instructions,
"int f(int a, int b) { return a & b; }",
)
.require("and i32", "and i32"),
X86IRGenTest::new(
"ir_bitwise_or",
X86IRGenCategory::Instructions,
"int f(int a, int b) { return a | b; }",
)
.require("or i32", "or i32"),
X86IRGenTest::new(
"ir_bitwise_xor",
X86IRGenCategory::Instructions,
"int f(int a, int b) { return a ^ b; }",
)
.require("xor i32", "xor i32"),
X86IRGenTest::new(
"ir_bitwise_not",
X86IRGenCategory::Instructions,
"int f(int a) { return ~a; }",
)
.require("xor -1 pattern for bitwise not", "xor")
.require("constant -1 operand", "-1"),
X86IRGenTest::new(
"ir_bitwise_and_i8",
X86IRGenCategory::Instructions,
"char f(char a, char b) { return a & b; }",
)
.require("and on promoted i8", "and"),
X86IRGenTest::new(
"ir_bitwise_or_i64",
X86IRGenCategory::Instructions,
"long long f(long long a, long long b) { return a | b; }",
)
.require("or i64", "or i64"),
X86IRGenTest::new(
"ir_bitwise_xor_i16",
X86IRGenCategory::Instructions,
"short f(short a, short b) { return a ^ b; }",
)
.require("xor on promoted i16", "xor"),
X86IRGenTest::new(
"ir_bitwise_shl_by_literal",
X86IRGenCategory::Instructions,
"int f(int a) { return a << 4; }",
)
.require("shl by constant 4", "shl i32"),
X86IRGenTest::new(
"ir_bitwise_lshr_by_literal",
X86IRGenCategory::Instructions,
"unsigned f(unsigned a) { return a >> 8; }",
)
.require("lshr by constant 8", "lshr i32"),
X86IRGenTest::new(
"ir_bitwise_ashr_by_literal",
X86IRGenCategory::Instructions,
"int f(int a) { return a >> 2; }",
)
.require("ashr by constant 2", "ashr i32"),
X86IRGenTest::new(
"ir_bitwise_rotate_left",
X86IRGenCategory::Instructions,
"unsigned f(unsigned x, unsigned n) { return (x << n) | (x >> (32 - n)); }",
)
.require("rotate left pattern", "shl")
.require("lshr for rotate right part", "lshr"),
X86IRGenTest::new(
"ir_bitwise_rotate_right",
X86IRGenCategory::Instructions,
"unsigned f(unsigned x, unsigned n) { return (x >> n) | (x << (32 - n)); }",
)
.require("rotate right pattern", "lshr")
.require("shl for rotate left part", "shl"),
X86IRGenTest::new(
"ir_bitwise_popcount_builtin",
X86IRGenCategory::Instructions,
"int f(unsigned x) { return __builtin_popcount(x); }",
)
.require("popcount intrinsic", "llvm.ctpop"),
X86IRGenTest::new(
"ir_bitwise_clz_builtin",
X86IRGenCategory::Instructions,
"int f(unsigned x) { return __builtin_clz(x); }",
)
.require("clz intrinsic", "llvm.ctlz"),
X86IRGenTest::new(
"ir_bitwise_ctz_builtin",
X86IRGenCategory::Instructions,
"int f(unsigned x) { return __builtin_ctz(x); }",
)
.require("ctz intrinsic", "llvm.cttz"),
X86IRGenTest::new(
"ir_bitwise_bswap_builtin",
X86IRGenCategory::Instructions,
"unsigned f(unsigned x) { return __builtin_bswap32(x); }",
)
.require("bswap intrinsic", "llvm.bswap"),
X86IRGenTest::new(
"ir_bitwise_parity_builtin",
X86IRGenCategory::Instructions,
"int f(unsigned x) { return __builtin_parity(x); }",
)
.require("parity intrinsic", "llvm.ctpop"),
]
}
pub fn build_comparison_tests() -> Vec<X86IRGenTest> {
vec![
X86IRGenTest::new(
"ir_cmp_icmp_eq",
X86IRGenCategory::Instructions,
"int f(int a, int b) { return a == b; }",
)
.require("icmp eq", "icmp eq i32"),
X86IRGenTest::new(
"ir_cmp_icmp_ne",
X86IRGenCategory::Instructions,
"int f(int a, int b) { return a != b; }",
)
.require("icmp ne", "icmp ne i32"),
X86IRGenTest::new(
"ir_cmp_icmp_sgt",
X86IRGenCategory::Instructions,
"int f(int a, int b) { return a > b; }",
)
.require("icmp sgt", "icmp sgt i32"),
X86IRGenTest::new(
"ir_cmp_icmp_sge",
X86IRGenCategory::Instructions,
"int f(int a, int b) { return a >= b; }",
)
.require("icmp sge", "icmp sge i32"),
X86IRGenTest::new(
"ir_cmp_icmp_slt",
X86IRGenCategory::Instructions,
"int f(int a, int b) { return a < b; }",
)
.require("icmp slt", "icmp slt i32"),
X86IRGenTest::new(
"ir_cmp_icmp_sle",
X86IRGenCategory::Instructions,
"int f(int a, int b) { return a <= b; }",
)
.require("icmp sle", "icmp sle i32"),
X86IRGenTest::new(
"ir_cmp_icmp_ugt",
X86IRGenCategory::Instructions,
"unsigned f(unsigned a, unsigned b) { return a > b; }",
)
.require("icmp ugt for unsigned >", "icmp ugt i32"),
X86IRGenTest::new(
"ir_cmp_icmp_uge",
X86IRGenCategory::Instructions,
"unsigned f(unsigned a, unsigned b) { return a >= b; }",
)
.require("icmp uge for unsigned >=", "icmp uge i32"),
X86IRGenTest::new(
"ir_cmp_icmp_ult",
X86IRGenCategory::Instructions,
"unsigned f(unsigned a, unsigned b) { return a < b; }",
)
.require("icmp ult for unsigned <", "icmp ult i32"),
X86IRGenTest::new(
"ir_cmp_icmp_ule",
X86IRGenCategory::Instructions,
"unsigned f(unsigned a, unsigned b) { return a <= b; }",
)
.require("icmp ule for unsigned <=", "icmp ule i32"),
X86IRGenTest::new(
"ir_cmp_fcmp_oeq",
X86IRGenCategory::Instructions,
"int f(float a, float b) { return a == b; }",
)
.require("fcmp oeq", "fcmp oeq float"),
X86IRGenTest::new(
"ir_cmp_fcmp_one",
X86IRGenCategory::Instructions,
"int f(float a, float b) { return a != b; }",
)
.require("fcmp one", "fcmp one float"),
X86IRGenTest::new(
"ir_cmp_fcmp_ogt",
X86IRGenCategory::Instructions,
"int f(float a, float b) { return a > b; }",
)
.require("fcmp ogt", "fcmp ogt float"),
X86IRGenTest::new(
"ir_cmp_fcmp_oge",
X86IRGenCategory::Instructions,
"int f(float a, float b) { return a >= b; }",
)
.require("fcmp oge", "fcmp oge float"),
X86IRGenTest::new(
"ir_cmp_fcmp_olt",
X86IRGenCategory::Instructions,
"int f(float a, float b) { return a < b; }",
)
.require("fcmp olt", "fcmp olt float"),
X86IRGenTest::new(
"ir_cmp_fcmp_ole",
X86IRGenCategory::Instructions,
"int f(float a, float b) { return a <= b; }",
)
.require("fcmp ole", "fcmp ole float"),
X86IRGenTest::new(
"ir_cmp_fcmp_ord",
X86IRGenCategory::Instructions,
"#include <math.h>\nint f(float x) { return !isnan(x); }",
)
.require("fcmp ord", "fcmp"),
X86IRGenTest::new(
"ir_cmp_fcmp_uno",
X86IRGenCategory::Instructions,
"#include <math.h>\nint f(float x) { return isnan(x); }",
)
.require("fcmp uno", "fcmp"),
X86IRGenTest::new(
"ir_cmp_fcmp_ueq",
X86IRGenCategory::Instructions,
"int f(double a, double b) { return !(a < b) && !(a > b); }",
)
.require("fcmp instruction for complex compare", "fcmp"),
X86IRGenTest::new(
"ir_cmp_fcmp_double",
X86IRGenCategory::Instructions,
"int f(double a, double b) { return a > b; }",
)
.require("fcmp ogt double", "fcmp ogt double"),
X86IRGenTest::new(
"ir_cmp_icmp_ptr_eq",
X86IRGenCategory::Instructions,
"int f(int *a, int *b) { return a == b; }",
)
.require("icmp eq on pointer", "icmp eq"),
X86IRGenTest::new(
"ir_cmp_icmp_ptr_ugt",
X86IRGenCategory::Instructions,
"int f(int *a, int *b) { return a > b; }",
)
.require("pointer comparison uses unsigned", "icmp ugt"),
X86IRGenTest::new(
"ir_cmp_icmp_ptr_ult",
X86IRGenCategory::Instructions,
"int f(int *a, int *b) { return a < b; }",
)
.require("pointer less-than unsigned", "icmp ult"),
X86IRGenTest::new(
"ir_cmp_chained_lt",
X86IRGenCategory::Instructions,
"int f(int a, int b, int c) { return a < b && b < c; }",
)
.require("chained comparison", "icmp")
.require_at_least("two comparisons", "icmp", 2),
X86IRGenTest::new(
"ir_cmp_icmp_const_rhs",
X86IRGenCategory::Instructions,
"int f(int a) { return a > 5; }",
)
.require("compare with constant 5", "icmp sgt i32")
.require("constant operand", "5"),
X86IRGenTest::new(
"ir_cmp_icmp_const_lhs",
X86IRGenCategory::Instructions,
"int f(int a) { return 0 != a; }",
)
.require("compare with zero", "icmp ne i32")
.require("zero operand", "0"),
X86IRGenTest::new(
"ir_cmp_fcmp_double_oeq",
X86IRGenCategory::Instructions,
"int f(double a, double b) { return a == b; }",
)
.require("ordered and equal", "fcmp oeq double"),
X86IRGenTest::new(
"ir_cmp_fcmp_double_one",
X86IRGenCategory::Instructions,
"int f(double a, double b) { return a != b; }",
)
.require("ordered and not equal", "fcmp one double"),
X86IRGenTest::new(
"ir_cmp_fcmp_double_oge",
X86IRGenCategory::Instructions,
"int f(double a, double b) { return a >= b; }",
)
.require("ordered greater or equal", "fcmp oge double"),
X86IRGenTest::new(
"ir_cmp_fcmp_double_ugt",
X86IRGenCategory::Instructions,
"int f(double a, double b) { return !(a <= b); }",
)
.require("fcmp for unordered >", "fcmp"),
X86IRGenTest::new(
"ir_cmp_memcmp",
X86IRGenCategory::Instructions,
"#include <string.h>\nint f(const void *a, const void *b) { return memcmp(a, b, 4); }",
)
.require("memcmp call", "call"),
X86IRGenTest::new(
"ir_cmp_three_way_int",
X86IRGenCategory::Instructions,
"int f(int a, int b) { return (a > b) - (a < b); }",
)
.require("three-way comparison pattern", "icmp"),
]
}
pub fn build_control_flow_tests() -> Vec<X86IRGenTest> {
vec![
X86IRGenTest::new("ir_cf_ret_void", X86IRGenCategory::ControlFlow,
"void f(void) {}")
.require("ret void", "ret void"),
X86IRGenTest::new("ir_cf_ret_int", X86IRGenCategory::ControlFlow,
"int f(void) { return 0; }")
.require("ret i32", "ret i32"),
X86IRGenTest::new("ir_cf_ret_float", X86IRGenCategory::ControlFlow,
"float f(void) { return 0.0f; }")
.require("ret float", "ret float"),
X86IRGenTest::new("ir_cf_br_unconditional", X86IRGenCategory::ControlFlow,
"void f(int x) { goto label; label: return; }")
.require("unconditional branch", "br label"),
X86IRGenTest::new("ir_cf_br_conditional_if", X86IRGenCategory::ControlFlow,
"int f(int x) { if (x) return 1; else return 0; }")
.require("conditional branch", "br i1"),
X86IRGenTest::new("ir_cf_br_conditional_while", X86IRGenCategory::ControlFlow,
"int f(int n) { int s = 0; while (n > 0) { s += n; n--; } return s; }")
.require("conditional branch in loop", "br i1"),
X86IRGenTest::new("ir_cf_switch_simple", X86IRGenCategory::ControlFlow,
"int f(int x) { switch(x) { case 1: return 10; case 2: return 20; default: return 0; } }")
.require("switch instruction", "switch i32"),
X86IRGenTest::new("ir_cf_switch_multi_case", X86IRGenCategory::ControlFlow,
"int f(int x) { switch(x) { case 1: case 2: case 3: return 42; default: return 0; } }")
.require("switch instruction", "switch"),
X86IRGenTest::new("ir_cf_switch_default_only", X86IRGenCategory::ControlFlow,
"int f(int x) { switch(x) { default: return 99; } }")
.require("switch with default only", "switch"),
X86IRGenTest::new("ir_cf_phi_if_else", X86IRGenCategory::ControlFlow,
"int f(int x) { int y; if (x > 0) y = 1; else y = 2; return y; }")
.require("phi node for if-else assignment", "phi i32"),
X86IRGenTest::new("ir_cf_phi_for_loop", X86IRGenCategory::ControlFlow,
"int f(int n) { int sum = 0; for (int i = 0; i < n; i++) sum += i; return sum; }")
.require("phi nodes for loop induction", "phi i32"),
X86IRGenTest::new("ir_cf_unreachable", X86IRGenCategory::ControlFlow,
"void f(void) { __builtin_unreachable(); }")
.require("unreachable instruction", "unreachable"),
X86IRGenTest::new("ir_cf_do_while", X86IRGenCategory::ControlFlow,
"int f(int n) { int i = 0; do { i++; } while (i < n); return i; }")
.require("do-while loop", "phi i32")
.require("conditional branch for loop exit", "br i1"),
X86IRGenTest::new("ir_cf_for_loop_init_cond_inc", X86IRGenCategory::ControlFlow,
"int f(int n) { int s = 0; for (int i = 0; i < n; i += 2) s += i; return s; }")
.require("for loop with step", "phi i32"),
X86IRGenTest::new("ir_cf_infinite_loop", X86IRGenCategory::ControlFlow,
"void f(void) { while(1) {} }")
.require("infinite loop", "br label"),
X86IRGenTest::new("ir_cf_nested_loops", X86IRGenCategory::ControlFlow,
"int f(int n) { int s = 0; for (int i = 0; i < n; i++) for (int j = 0; j < i; j++) s++; return s; }")
.require("nested for loops", "br label")
.require_at_least("multiple phi nodes", "phi", 2),
X86IRGenTest::new("ir_cf_break_in_loop", X86IRGenCategory::ControlFlow,
"int f(int n) { for (int i = 0; ; i++) { if (i >= n) break; } return 0; }")
.require("break from loop", "br"),
X86IRGenTest::new("ir_cf_continue_in_loop", X86IRGenCategory::ControlFlow,
"int f(int n) { int s = 0; for (int i = 0; i < n; i++) { if (i % 2 == 0) continue; s += i; } return s; }")
.require("continue in loop", "br"),
X86IRGenTest::new("ir_cf_early_return", X86IRGenCategory::ControlFlow,
"int f(int x) { if (x < 0) return -1; if (x == 0) return 0; return 1; }")
.require("multiple return points", "ret i32")
.require_exact("three returns", "ret i32", 3),
X86IRGenTest::new("ir_cf_goto_forward", X86IRGenCategory::ControlFlow,
"void f(void) { goto label; label: return; }")
.require("forward goto via br", "br label"),
X86IRGenTest::new("ir_cf_goto_backward", X86IRGenCategory::ControlFlow,
"void f(int n) { int i = 0; start: if (i++ < n) goto start; }")
.require("backward goto via br", "br label"),
X86IRGenTest::new("ir_cf_switch_dense", X86IRGenCategory::ControlFlow,
"int f(int x) { switch(x) { case 0: return 1; case 1: return 2; case 2: return 3; case 3: return 4; case 4: return 5; default: return 0; } }")
.require("dense switch", "switch i32"),
X86IRGenTest::new("ir_cf_switch_sparse", X86IRGenCategory::ControlFlow,
"int f(int x) { switch(x) { case -100: return 1; case 0: return 2; case 100: return 3; case 1000: return 4; default: return 0; } }")
.require("sparse switch", "switch i32"),
X86IRGenTest::new("ir_cf_short_circuit_and", X86IRGenCategory::ControlFlow,
"int f(int a, int b, int c) { return a && b && c; }")
.require("short-circuit and", "br i1"),
X86IRGenTest::new("ir_cf_short_circuit_or", X86IRGenCategory::ControlFlow,
"int f(int a, int b, int c) { return a || b || c; }")
.require("short-circuit or", "br i1"),
X86IRGenTest::new("ir_cf_comma_operator", X86IRGenCategory::ControlFlow,
"int f(int a, int b) { return (a++, a += b, a); }")
.require("comma operator sequencing", "add"),
]
}
pub fn build_aggregate_tests() -> Vec<X86IRGenTest> {
vec![
X86IRGenTest::new("ir_agg_extractvalue_struct", X86IRGenCategory::Instructions,
"struct S { int a; double b; }; int f(struct S s) { return s.a; }")
.require("extractvalue for struct field", "extractvalue"),
X86IRGenTest::new("ir_agg_insertvalue_struct", X86IRGenCategory::Instructions,
"struct S { int a; int b; }; struct S f(int x) { struct S s; s.a = x; s.b = 0; return s; }")
.require("insertvalue for struct field", "insertvalue"),
X86IRGenTest::new("ir_agg_extractelement", X86IRGenCategory::Instructions,
"typedef int v4si __attribute__((vector_size(16))); int f(v4si v) { return v[2]; }")
.require("extractelement instruction", "extractelement"),
X86IRGenTest::new("ir_agg_insertelement", X86IRGenCategory::Instructions,
"typedef int v4si __attribute__((vector_size(16))); v4si f(v4si v, int x) { v[1] = x; return v; }")
.require("insertelement instruction", "insertelement"),
X86IRGenTest::new("ir_agg_shufflevector", X86IRGenCategory::Instructions,
"typedef int v4si __attribute__((vector_size(16))); v4si f(v4si a, v4si b) { return __builtin_shufflevector(a, b, 0, 5, 2, 7); }")
.require("shufflevector instruction", "shufflevector"),
X86IRGenTest::new("ir_agg_shufflevector_identity", X86IRGenCategory::Instructions,
"typedef int v4si __attribute__((vector_size(16))); v4si f(v4si a) { return __builtin_shufflevector(a, a, 0, 1, 2, 3); }")
.require("identity shuffle", "shufflevector"),
X86IRGenTest::new("ir_agg_shufflevector_reverse", X86IRGenCategory::Instructions,
"typedef int v4si __attribute__((vector_size(16))); v4si f(v4si a) { return __builtin_shufflevector(a, a, 3, 2, 1, 0); }")
.require("reverse shuffle", "shufflevector"),
X86IRGenTest::new("ir_agg_shufflevector_interleave", X86IRGenCategory::Instructions,
"typedef int v4si __attribute__((vector_size(16))); v4si f(v4si a, v4si b) { return __builtin_shufflevector(a, b, 0, 4, 1, 5); }")
.require("interleave shuffle", "shufflevector"),
X86IRGenTest::new("ir_agg_extractvalue_array", X86IRGenCategory::Instructions,
"struct S { int arr[3]; }; int f(struct S s) { return s.arr[1]; }")
.require("extractvalue from array within struct", "extractvalue"),
X86IRGenTest::new("ir_agg_union_bitcast", X86IRGenCategory::Instructions,
"union U { int i; float f; }; float f(union U u) { return u.f; }")
.require("union type punning may use bitcast", "load"),
]
}
pub fn build_call_tests() -> Vec<X86IRGenTest> {
vec![
X86IRGenTest::new("ir_call_direct_void", X86IRGenCategory::Instructions,
"void g(void); void f(void) { g(); }")
.require("call void @g", "call void"),
X86IRGenTest::new("ir_call_direct_args", X86IRGenCategory::Instructions,
"int add(int a, int b) { return a + b; } int f(int x, int y) { return add(x, y); }")
.require("call to add with arguments", "call i32 @add"),
X86IRGenTest::new("ir_call_direct_float", X86IRGenCategory::Instructions,
"double sin(double x); double f(double a) { return sin(a); }")
.require("call to sin", "call double"),
X86IRGenTest::new("ir_call_indirect_simple", X86IRGenCategory::Instructions,
"int f(int (*fn)(int), int x) { return fn(x); }")
.require("indirect call via function pointer", "call i32"),
X86IRGenTest::new("ir_call_indirect_void", X86IRGenCategory::Instructions,
"void f(void (*fn)(void)) { fn(); }")
.require("indirect call void", "call void"),
X86IRGenTest::new("ir_call_varargs", X86IRGenCategory::Instructions,
"#include <stdarg.h>\nint f(int n, ...) { va_list ap; va_start(ap, n); int s = va_arg(ap, int); va_end(ap); return s; }")
.require("varargs function definition", "va_start"),
X86IRGenTest::new("ir_call_return_value", X86IRGenCategory::Instructions,
"int g(void) { return 42; } int f(void) { return g(); }")
.require("return from call result", "%call")
.require("ret i32", "ret i32"),
X86IRGenTest::new("ir_call_tail_call", X86IRGenCategory::Instructions,
"int g(int x); int f(int x) { return g(x); }")
.with_opt(X86OptLevel::O2)
.require("tail call optimization", "tail call"),
X86IRGenTest::new("ir_call_noreturn", X86IRGenCategory::Instructions,
"void f(void) __attribute__((noreturn)); void g(void) { f(); __builtin_unreachable(); }")
.require("call to noreturn", "call void"),
]
}
pub fn build_memory_attr_tests() -> Vec<X86IRGenTest> {
vec![
X86IRGenTest::new("ir_mem_aligned_load", X86IRGenCategory::MemoryAccess,
"int f(int *p) { return *p; }")
.require("aligned load (default)", "align 4"),
X86IRGenTest::new("ir_mem_aligned_store", X86IRGenCategory::MemoryAccess,
"void f(int *p) { *p = 42; }")
.require("aligned store (default)", "align 4"),
X86IRGenTest::new("ir_mem_aligned_load_8", X86IRGenCategory::MemoryAccess,
"double f(double *p) { return *p; }")
.require("aligned load align 8", "align 8"),
X86IRGenTest::new("ir_mem_aligned_load_1", X86IRGenCategory::MemoryAccess,
"char f(char *p) { return *p; }")
.require("aligned load align 1", "align 1"),
X86IRGenTest::new("ir_mem_unaligned_load", X86IRGenCategory::MemoryAccess,
"struct __attribute__((packed)) S { int a; char b; }; int f(struct S *s) { return s->a; }")
.require("unaligned load via packed struct", "load i32"),
X86IRGenTest::new("ir_mem_volatile_load", X86IRGenCategory::MemoryAccess,
"int f(volatile int *p) { return *p; }")
.require("volatile load", "volatile"),
X86IRGenTest::new("ir_mem_volatile_store", X86IRGenCategory::MemoryAccess,
"void f(volatile int *p, int v) { *p = v; }")
.require("volatile store", "volatile"),
X86IRGenTest::new("ir_mem_atomic_load", X86IRGenCategory::Atomics,
"#include <stdatomic.h>\nint f(_Atomic int *p) { return atomic_load(p); }")
.require("atomic load", "load atomic"),
X86IRGenTest::new("ir_mem_atomic_store", X86IRGenCategory::Atomics,
"#include <stdatomic.h>\nvoid f(_Atomic int *p, int v) { atomic_store(p, v); }")
.require("atomic store", "store atomic"),
X86IRGenTest::new("ir_mem_atomic_xchg", X86IRGenCategory::Atomics,
"#include <stdatomic.h>\nint f(_Atomic int *p, int v) { return atomic_exchange(p, v); }")
.require("atomic exchange", "atomicrmw xchg"),
X86IRGenTest::new("ir_mem_atomic_add", X86IRGenCategory::Atomics,
"#include <stdatomic.h>\nint f(_Atomic int *p, int v) { return atomic_fetch_add(p, v); }")
.require("atomic fetch add", "atomicrmw add"),
X86IRGenTest::new("ir_mem_atomic_sub", X86IRGenCategory::Atomics,
"#include <stdatomic.h>\nint f(_Atomic int *p, int v) { return atomic_fetch_sub(p, v); }")
.require("atomic fetch sub", "atomicrmw sub"),
X86IRGenTest::new("ir_mem_atomic_cmpxchg", X86IRGenCategory::Atomics,
"#include <stdatomic.h>\nint f(_Atomic int *p, int expected, int desired) { atomic_compare_exchange_strong(p, &expected, desired); return expected; }")
.require("cmpxchg instruction", "cmpxchg"),
X86IRGenTest::new("ir_mem_fence", X86IRGenCategory::Atomics,
"#include <stdatomic.h>\nvoid f(void) { atomic_thread_fence(memory_order_seq_cst); }")
.require("fence instruction", "fence"),
X86IRGenTest::new("ir_mem_ordering_acquire", X86IRGenCategory::Atomics,
"#include <stdatomic.h>\nint f(_Atomic int *p) { return atomic_load_explicit(p, memory_order_acquire); }")
.require("acquire ordering", "acquire"),
X86IRGenTest::new("ir_mem_ordering_release", X86IRGenCategory::Atomics,
"#include <stdatomic.h>\nvoid f(_Atomic int *p, int v) { atomic_store_explicit(p, v, memory_order_release); }")
.require("release ordering", "release"),
X86IRGenTest::new("ir_mem_ordering_seq_cst", X86IRGenCategory::Atomics,
"#include <stdatomic.h>\nint f(_Atomic int *p, int v) { return atomic_fetch_add_explicit(p, v, memory_order_seq_cst); }")
.require("seq_cst ordering", "seq_cst"),
X86IRGenTest::new("ir_mem_ordering_monotonic", X86IRGenCategory::Atomics,
"#include <stdatomic.h>\nint f(_Atomic int *p) { return atomic_load_explicit(p, memory_order_relaxed); }")
.require("relaxed/monotonic ordering", "monotonic"),
]
}
pub fn build_function_attr_tests() -> Vec<X86IRGenTest> {
vec![
X86IRGenTest::new("ir_attr_nounwind", X86IRGenCategory::FunctionAttributes,
"int f(int x) __attribute__((nothrow)); int f(int x) { return x; }")
.require("nounwind attribute", "nounwind"),
X86IRGenTest::new("ir_attr_readonly", X86IRGenCategory::FunctionAttributes,
"int f(const int *p) __attribute__((pure)); int f(const int *p) { return *p; }")
.require("readonly attribute", "readonly"),
X86IRGenTest::new("ir_attr_readnone", X86IRGenCategory::FunctionAttributes,
"int f(int x) __attribute__((const)); int f(int x) { return x * 2; }")
.require("readnone attribute", "readnone"),
X86IRGenTest::new("ir_attr_noinline", X86IRGenCategory::FunctionAttributes,
"int f(int x) __attribute__((noinline)); int f(int x) { return x; }")
.require("noinline attribute in IR", "noinline"),
X86IRGenTest::new("ir_attr_alwaysinline", X86IRGenCategory::FunctionAttributes,
"__attribute__((always_inline)) int f(int x) { return x; } int g(void) { return f(42); }")
.require("alwaysinline attribute", "alwaysinline"),
X86IRGenTest::new("ir_attr_optsize", X86IRGenCategory::FunctionAttributes,
"int f(void) __attribute__((optimize(\"Os\"))); int f(void) { return 42; }")
.require("optsize attribute for Os", "optsize"),
X86IRGenTest::new("ir_attr_minsize", X86IRGenCategory::FunctionAttributes,
"int f(void) __attribute__((optimize(\"Oz\"))); int f(void) { return 42; }")
.require("minsize attribute for Oz", "minsize"),
X86IRGenTest::new("ir_attr_ssp", X86IRGenCategory::FunctionAttributes,
"void f(char *buf) { buf[0] = 'x'; }")
.with_flags(vec!["-fstack-protector-strong"])
.require("ssp attribute for stack protector", "sspstrong"),
X86IRGenTest::new("ir_attr_sspreq", X86IRGenCategory::FunctionAttributes,
"void f(char *buf) { buf[0] = 'x'; }")
.with_flags(vec!["-fstack-protector-all"])
.require("sspreq attribute for stack protector all", "sspreq"),
X86IRGenTest::new("ir_attr_uwtable", X86IRGenCategory::FunctionAttributes,
"void f(void) {}")
.require("uwtable attribute", "uwtable"),
X86IRGenTest::new("ir_attr_sanitize_address", X86IRGenCategory::FunctionAttributes,
"int f(int *p) { return *p; }")
.with_flags(vec!["-fsanitize=address"])
.require("sanitize_address attribute", "sanitize_address"),
X86IRGenTest::new("ir_attr_sanitize_memory", X86IRGenCategory::FunctionAttributes,
"int f(int *p) { return *p; }")
.with_flags(vec!["-fsanitize=memory"])
.require("sanitize_memory attribute", "sanitize_memory"),
X86IRGenTest::new("ir_attr_sanitize_thread", X86IRGenCategory::FunctionAttributes,
"int f(int *p) { return *p; }")
.with_flags(vec!["-fsanitize=thread"])
.require("sanitize_thread attribute", "sanitize_thread"),
X86IRGenTest::new("ir_attr_sanitize_undefined", X86IRGenCategory::FunctionAttributes,
"int f(int x) { return x + 1; }")
.with_flags(vec!["-fsanitize=undefined"])
.require("sanitize function check", "llvm."),
X86IRGenTest::new("ir_attr_noreturn", X86IRGenCategory::FunctionAttributes,
"void f(void) __attribute__((noreturn)); void f(void) { while(1); }")
.require("noreturn attribute", "noreturn"),
X86IRGenTest::new("ir_attr_cold", X86IRGenCategory::FunctionAttributes,
"void f(void) __attribute__((cold)); void f(void) {}")
.require("cold attribute", "cold"),
X86IRGenTest::new("ir_attr_hot", X86IRGenCategory::FunctionAttributes,
"void f(void) __attribute__((hot)); void f(void) {}")
.require("hot attribute", "hot"),
X86IRGenTest::new("ir_attr_returns_twice", X86IRGenCategory::FunctionAttributes,
"int f(void) { return setjmp(0); }")
.require("returns_twice on setjmp", "returns_twice"),
X86IRGenTest::new("ir_attr_nocapture", X86IRGenCategory::FunctionAttributes,
"void f(int *__restrict p) { *p = 0; }")
.require("noalias on restrict pointer", "noalias"),
X86IRGenTest::new("ir_attr_optnone", X86IRGenCategory::FunctionAttributes,
"int f(int x) __attribute__((optnone)); int f(int x) { return x; }")
.require("optnone attribute", "optnone"),
]
}
pub fn build_param_attr_tests() -> Vec<X86IRGenTest> {
vec![
X86IRGenTest::new("ir_param_zeroext_i1", X86IRGenCategory::ParameterAttributes,
"_Bool f(_Bool x) { return x; }")
.require("zeroext for i1", "zeroext"),
X86IRGenTest::new("ir_param_zeroext_i8", X86IRGenCategory::ParameterAttributes,
"unsigned char f(unsigned char c) { return c; }")
.require("zeroext for unsigned char", "zeroext"),
X86IRGenTest::new("ir_param_zeroext_i16", X86IRGenCategory::ParameterAttributes,
"unsigned short f(unsigned short s) { return s; }")
.require("zeroext for unsigned short", "zeroext"),
X86IRGenTest::new("ir_param_signext_i8", X86IRGenCategory::ParameterAttributes,
"signed char f(signed char c) { return c; }")
.require("signext for signed char", "signext"),
X86IRGenTest::new("ir_param_signext_i16", X86IRGenCategory::ParameterAttributes,
"short f(short s) { return s; }")
.require("signext for short", "signext"),
X86IRGenTest::new("ir_param_noalias_restrict", X86IRGenCategory::ParameterAttributes,
"void f(int *__restrict p, int *__restrict q) { *p = *q; }")
.require("noalias on restrict pointer", "noalias"),
X86IRGenTest::new("ir_param_sret_large_struct", X86IRGenCategory::ParameterAttributes,
"struct Large { int a; int b; int c; int d; int e; }; struct Large f(void) { struct Large x = {0}; return x; }")
.require("sret parameter for large struct return", "sret"),
X86IRGenTest::new("ir_param_byval_struct", X86IRGenCategory::ParameterAttributes,
"struct S { int a; double b; }; double f(struct S s) { return s.b; }")
.require("byval for passed struct", "byval"),
X86IRGenTest::new("ir_param_inreg", X86IRGenCategory::ParameterAttributes,
"void f(int x __attribute__((regparm(3))), int y) {}")
.require("inreg attribute", "inreg"),
X86IRGenTest::new("ir_param_nest", X86IRGenCategory::ParameterAttributes,
"void f(void) { int x; void g(int *p) { *p = 1; } g(&x); }")
.require("nest attribute or alloca for captured var", "alloca"),
X86IRGenTest::new("ir_param_align", X86IRGenCategory::ParameterAttributes,
"void f(double *p __attribute__((align_value(64)))) { *p = 0.0; }")
.require("align attribute on param", "align"),
X86IRGenTest::new("ir_param_dereferenceable", X86IRGenCategory::ParameterAttributes,
"int f(int *p) { return *p; }")
.require("dereferenceable on pointer", "dereferenceable"),
]
}
pub fn build_linkage_tests() -> Vec<X86IRGenTest> {
vec![
X86IRGenTest::new("ir_linkage_external", X86IRGenCategory::Linkage,
"int f(int x) { return x; }")
.require("define dso_local with external linkage", "define"),
X86IRGenTest::new("ir_linkage_private_static", X86IRGenCategory::Linkage,
"static int f(int x) { return x; }")
.require("private/internal linkage for static", "internal"),
X86IRGenTest::new("ir_linkage_internal_anon", X86IRGenCategory::Linkage,
"static int g(void) { return 42; } int f(void) { return g(); }")
.require("internal linkage for static function", "internal"),
X86IRGenTest::new("ir_linkage_inline", X86IRGenCategory::Linkage,
"inline int f(int x) { return x; }")
.require("linkonce_odr for inline", "linkonce_odr"),
X86IRGenTest::new("ir_linkage_weak", X86IRGenCategory::Linkage,
"int f(int x) __attribute__((weak)); int f(int x) { return x; }")
.require("weak linkage", "weak"),
X86IRGenTest::new("ir_linkage_weak_odr", X86IRGenCategory::Linkage,
"inline __attribute__((weak)) int f(int x) { return x; }")
.require("weak_odr linkage", "weak_odr"),
X86IRGenTest::new("ir_linkage_available_externally", X86IRGenCategory::Linkage,
"extern inline int f(int x) { return x; } int g(void) { return f(42); }")
.require("available_externally", "available_externally"),
X86IRGenTest::new("ir_linkage_linkonce", X86IRGenCategory::Linkage,
"inline int f(void) { static int x = 0; return x++; }")
.require("linkonce linkage for inline", "linkonce"),
X86IRGenTest::new("ir_linkage_extern_weak", X86IRGenCategory::Linkage,
"extern int f(int x) __attribute__((weak)); int g(void) { if (f) return f(1); return 0; }")
.require("extern_weak linkage", "extern_weak"),
X86IRGenTest::new("ir_linkage_common", X86IRGenCategory::Linkage,
"int x; int f(void) { return x; }")
.require("common linkage for tentative def", "common"),
X86IRGenTest::new("ir_linkage_appending", X86IRGenCategory::Linkage,
"struct S { int x; }; struct S s __attribute__((section(\".data\"))) = {42};")
.require("global with section attribute", "@s"),
]
}
pub fn build_visibility_tests() -> Vec<X86IRGenTest> {
vec![
X86IRGenTest::new(
"ir_vis_default",
X86IRGenCategory::Visibility,
"int f(int x) { return x; }",
)
.require("default visibility (no hidden/protected)", "define"),
X86IRGenTest::new(
"ir_vis_hidden",
X86IRGenCategory::Visibility,
"int f(int x) __attribute__((visibility(\"hidden\"))); int f(int x) { return x; }",
)
.require("hidden visibility", "hidden"),
X86IRGenTest::new(
"ir_vis_protected",
X86IRGenCategory::Visibility,
"int f(int x) __attribute__((visibility(\"protected\"))); int f(int x) { return x; }",
)
.require("protected visibility", "protected"),
X86IRGenTest::new(
"ir_vis_hidden_global",
X86IRGenCategory::Visibility,
"int x __attribute__((visibility(\"hidden\"))) = 42;",
)
.require("hidden visibility on global", "hidden"),
X86IRGenTest::new(
"ir_vis_protected_global",
X86IRGenCategory::Visibility,
"int x __attribute__((visibility(\"protected\"))) = 42;",
)
.require("protected visibility on global", "protected"),
X86IRGenTest::new(
"ir_vis_fvisibility_hidden",
X86IRGenCategory::Visibility,
"int f(int x) { return x; }",
)
.with_flags(vec!["-fvisibility=hidden"])
.require("hidden visibility via flag", "hidden"),
]
}
pub fn build_calling_convention_tests() -> Vec<X86IRGenTest> {
vec![
X86IRGenTest::new("ir_cc_ccc", X86IRGenCategory::CallingConvention,
"int f(int x) { return x; }")
.require("C calling convention ccc", "ccc"),
X86IRGenTest::new("ir_cc_fastcc", X86IRGenCategory::CallingConvention,
"int f(int x) __attribute__((fastcall)); int f(int x) { return x; }")
.require("fastcc calling convention", "fastcc"),
X86IRGenTest::new("ir_cc_coldcc", X86IRGenCategory::CallingConvention,
"void f(void) __attribute__((cold)); void f(void) {}")
.require("coldcc calling convention", "coldcc"),
X86IRGenTest::new("ir_cc_x86_stdcallcc", X86IRGenCategory::CallingConvention,
"int f(int x) __attribute__((stdcall)); int f(int x) { return x; }")
.with_target("i686-unknown-linux-gnu")
.require("x86_stdcallcc calling convention", "x86_stdcallcc"),
X86IRGenTest::new("ir_cc_x86_fastcallcc", X86IRGenCategory::CallingConvention,
"int f(int a, int b) __attribute__((fastcall)); int f(int a, int b) { return a + b; }")
.with_target("i686-unknown-linux-gnu")
.require("x86_fastcallcc calling convention", "x86_fastcallcc"),
X86IRGenTest::new("ir_cc_x86_thiscallcc", X86IRGenCategory::CallingConvention,
"struct S { int x; int f(int a) __attribute__((thiscall)); }; int S::f(int a) { return x + a; }")
.with_target("i686-unknown-linux-gnu")
.require("x86_thiscallcc calling convention", "x86_thiscallcc"),
X86IRGenTest::new("ir_cc_x86_vectorcallcc", X86IRGenCategory::CallingConvention,
"int f(int a, double b) __attribute__((vectorcall)); int f(int a, double b) { return a; }")
.require("x86_vectorcallcc calling convention", "x86_vectorcallcc"),
X86IRGenTest::new("ir_cc_x86_regcallcc", X86IRGenCategory::CallingConvention,
"int f(int a, int b, int c) __attribute__((regcall)); int f(int a, int b, int c) { return a + b + c; }")
.require("x86_regcallcc calling convention", "x86_regcallcc"),
X86IRGenTest::new("ir_cc_preserve_all", X86IRGenCategory::CallingConvention,
"void f(void) __attribute__((preserve_all)); void f(void) {}")
.require("preserve_all calling convention", "preserve_allcc"),
X86IRGenTest::new("ir_cc_preserve_most", X86IRGenCategory::CallingConvention,
"void f(void) __attribute__((preserve_most)); void f(void) {}")
.require("preserve_most calling convention", "preserve_mostcc"),
X86IRGenTest::new("ir_cc_webkit_js", X86IRGenCategory::CallingConvention,
"void f(void) __attribute__((regparm(1))); void f(void) {}")
.require("regparm affects CC", "regparm"),
]
}
pub fn build_debug_info_tests() -> Vec<X86IRGenTest> {
vec![
X86IRGenTest::new(
"ir_debug_compile_unit",
X86IRGenCategory::DebugInfo,
"int f(int x) { return x; }",
)
.with_flags(vec!["-g"])
.require("DICompileUnit metadata", "DICompileUnit"),
X86IRGenTest::new(
"ir_debug_subprogram",
X86IRGenCategory::DebugInfo,
"int f(int x) { return x; }",
)
.with_flags(vec!["-g"])
.require("DISubprogram metadata", "DISubprogram"),
X86IRGenTest::new(
"ir_debug_file",
X86IRGenCategory::DebugInfo,
"int f(int x) { return x; }",
)
.with_flags(vec!["-g"])
.require("DIFile metadata", "DIFile"),
X86IRGenTest::new(
"ir_debug_lexical_block",
X86IRGenCategory::DebugInfo,
"int f(int x) { { int y = x; return y; } }",
)
.with_flags(vec!["-g"])
.require("DILexicalBlock metadata", "DILexicalBlock"),
X86IRGenTest::new(
"ir_debug_local_variable",
X86IRGenCategory::DebugInfo,
"int f(int x) { int y = x + 1; return y; }",
)
.with_flags(vec!["-g"])
.require("DILocalVariable metadata", "DILocalVariable"),
X86IRGenTest::new(
"ir_debug_location",
X86IRGenCategory::DebugInfo,
"int f(int x) { return x; }",
)
.with_flags(vec!["-g"])
.require("!dbg location", "!dbg"),
X86IRGenTest::new(
"ir_debug_declare",
X86IRGenCategory::DebugInfo,
"int f(void) { int x = 0; return x; }",
)
.with_flags(vec!["-g"])
.require("llvm.dbg.declare", "llvm.dbg.declare"),
X86IRGenTest::new(
"ir_debug_value",
X86IRGenCategory::DebugInfo,
"int f(int x) { x = x + 1; return x; }",
)
.with_flags(vec!["-g"])
.require("llvm.dbg.value", "llvm.dbg.value"),
X86IRGenTest::new(
"ir_debug_type_basic",
X86IRGenCategory::DebugInfo,
"int f(int x) { return x; }",
)
.with_flags(vec!["-g"])
.require("DIBasicType metadata", "DIBasicType"),
X86IRGenTest::new(
"ir_debug_derived_type",
X86IRGenCategory::DebugInfo,
"int f(int *p) { return *p; }",
)
.with_flags(vec!["-g"])
.require("DIDerivedType for pointer", "DIDerivedType"),
X86IRGenTest::new(
"ir_debug_composite_type_struct",
X86IRGenCategory::DebugInfo,
"struct S { int a; }; int f(struct S s) { return s.a; }",
)
.with_flags(vec!["-g"])
.require("DICompositeType for struct", "DICompositeType"),
X86IRGenTest::new(
"ir_debug_global_variable",
X86IRGenCategory::DebugInfo,
"int x = 42; int f(void) { return x; }",
)
.with_flags(vec!["-g"])
.require("DIGlobalVariable metadata", "DIGlobalVariable"),
X86IRGenTest::new(
"ir_debug_no_debug_info_without_g",
X86IRGenCategory::DebugInfo,
"int f(int x) { return x; }",
)
.forbid("no debug metadata without -g", "DIFile"),
]
}
pub fn build_optimization_tests() -> Vec<X86IRGenTest> {
vec![
X86IRGenTest::new(
"ir_opt_O0_no_opt",
X86IRGenCategory::Optimization,
"int f(int a, int b) { return a + b; }",
)
.with_opt(X86OptLevel::O0)
.require("alloca at O0 for local var", "alloca")
.forbid("no mem2reg at O0", "!tbaa"),
X86IRGenTest::new(
"ir_opt_O1_mem2reg",
X86IRGenCategory::Optimization,
"int f(int a, int b) { int x = a; int y = b; return x + y; }",
)
.with_opt(X86OptLevel::O1)
.require("add instruction preserved", "add i32"),
X86IRGenTest::new(
"ir_opt_O2_vectorization",
X86IRGenCategory::Optimization,
"void f(int *a, int *b, int n) { for (int i = 0; i < n; i++) a[i] = b[i] + 1; }",
)
.with_opt(X86OptLevel::O2)
.require("loop at O2", "br"),
X86IRGenTest::new(
"ir_opt_constant_propagation",
X86IRGenCategory::Optimization,
"int f(void) { return 2 + 3 * 4; }",
)
.with_opt(X86OptLevel::O1)
.require("constant folded at O1", "ret i32 14"),
X86IRGenTest::new(
"ir_opt_dead_code_elimination",
X86IRGenCategory::Optimization,
"int f(int x) { int y = x * 2; return x + 1; }",
)
.with_opt(X86OptLevel::O1)
.require("return dominate value", "ret i32"),
X86IRGenTest::new(
"ir_opt_inlining",
X86IRGenCategory::Optimization,
"static int g(int x) { return x * 2; } int f(int a) { return g(a) + g(1); }",
)
.with_opt(X86OptLevel::O1)
.require("inlined at O1 (no call to g)", "add"),
X86IRGenTest::new(
"ir_opt_simplify_cfg",
X86IRGenCategory::Optimization,
"int f(int x) { if (x) { return 1; } else { return 0; } }",
)
.with_opt(X86OptLevel::O1)
.require("select instruction after simplifycfg", "select i32"),
X86IRGenTest::new(
"ir_opt_loop_unroll",
X86IRGenCategory::Optimization,
"int f(void) { int s = 0; for (int i = 0; i < 4; i++) s += i; return s; }",
)
.with_opt(X86OptLevel::O2)
.require("loop unrolled or simplified", "ret i32 6"),
X86IRGenTest::new(
"ir_opt_gvn",
X86IRGenCategory::Optimization,
"int f(int *p, int *q) { return *p + *p; }",
)
.with_opt(X86OptLevel::O1)
.require("common load elimination", "load i32"),
X86IRGenTest::new(
"ir_opt_sroa",
X86IRGenCategory::Optimization,
"struct S { int a; int b; }; int f(struct S s) { return s.a + s.b; }",
)
.with_opt(X86OptLevel::O1)
.require("scalar replacement of aggregate", "add i32"),
X86IRGenTest::new(
"ir_opt_loop_rotation",
X86IRGenCategory::Optimization,
"int f(int n) { int s = 0; for (int i = 0; i < n; i++) s += i; return s; }",
)
.with_opt(X86OptLevel::O2)
.require("loop at O2", "br"),
X86IRGenTest::new(
"ir_opt_licm",
X86IRGenCategory::Optimization,
"int f(int *p, int n) { int s = 0; for (int i = 0; i < n; i++) s += *p; return s; }",
)
.with_opt(X86OptLevel::O2)
.require("load hoisted out of loop at O2", "load"),
X86IRGenTest::new(
"ir_opt_indvar_simplify",
X86IRGenCategory::Optimization,
"int f(int n) { int s = 0; for (int i = 0; i < n; i++) s += i * 4; return s; }",
)
.with_opt(X86OptLevel::O1)
.require("induction variable simplification", "add"),
X86IRGenTest::new(
"ir_opt_tail_call",
X86IRGenCategory::Optimization,
"int g(int x); int f(int x) { return g(x); }",
)
.with_opt(X86OptLevel::O2)
.require("tail call optimization at O2", "tail call"),
X86IRGenTest::new(
"ir_opt_sink",
X86IRGenCategory::Optimization,
"int f(int x, int y) { int a = x * 2; if (y) return a; else return 0; }",
)
.with_opt(X86OptLevel::O1)
.require("expression sinking or hoisting", "mul i32"),
X86IRGenTest::new(
"ir_opt_Os_size",
X86IRGenCategory::Optimization,
"int f(int a, int b) { return a + b; }",
)
.with_flags(vec!["-Os"])
.require("optsize attribute at Os", "optsize"),
X86IRGenTest::new(
"ir_opt_Oz_size",
X86IRGenCategory::Optimization,
"int f(int a, int b) { return a + b; }",
)
.with_flags(vec!["-Oz"])
.require("minsize attribute at Oz", "minsize"),
]
}
#[derive(Debug, Clone)]
pub struct RoundtripTest {
pub name: String,
pub source: String,
pub ir_stable_across_roundtrip: bool,
}
impl RoundtripTest {
pub fn new(name: &str, source: &str) -> Self {
Self {
name: name.to_string(),
source: source.to_string(),
ir_stable_across_roundtrip: true,
}
}
}
pub fn build_roundtrip_tests() -> Vec<RoundtripTest> {
vec![
RoundtripTest::new("rt_int_return", "int f() { return 42; }"),
RoundtripTest::new("rt_float_add", "float f(float a, float b) { return a + b; }"),
RoundtripTest::new("rt_double_mul", "double f(double a, double b) { return a * b; }"),
RoundtripTest::new("rt_struct_pass", "struct S { int a; double b; }; double f(struct S s) { return s.b; }"),
RoundtripTest::new("rt_pointer_deref", "int f(int *p) { return *p; }"),
RoundtripTest::new("rt_array_access", "int f(int arr[10]) { return arr[5]; }"),
RoundtripTest::new("rt_conditional", "int f(int x) { return x ? 1 : 0; }"),
RoundtripTest::new("rt_loop", "int f(int n) { int s = 0; for(int i=0;i<n;i++) s+=i; return s; }"),
RoundtripTest::new("rt_switch", "int f(int x) { switch(x) { case 1: return 10; default: return 0; } }"),
RoundtripTest::new("rt_recursive", "int f(int n) { if(n<=1) return 1; return n*f(n-1); }"),
RoundtripTest::new("rt_void_func", "void f() {}"),
RoundtripTest::new("rt_global_var", "int x = 42; int f() { return x; }"),
RoundtripTest::new("rt_static_local", "int f() { static int x = 0; return x++; }"),
RoundtripTest::new("rt_bitwise", "int f(int a, int b) { return (a & b) | (a ^ b); }"),
RoundtripTest::new("rt_shift", "int f(int a) { return (a << 2) | (a >> 3); }"),
RoundtripTest::new("rt_vector_add", "typedef int v4si __attribute__((vector_size(16))); v4si f(v4si a, v4si b) { return a + b; }"),
RoundtripTest::new("rt_compound_literal", "struct S { int a; }; struct S f() { return (struct S){42}; }"),
RoundtripTest::new("rt_inline_func", "inline int add(int a, int b) { return a + b; } int f(int x, int y) { return add(x, y); }"),
RoundtripTest::new("rt_volatile", "int f(volatile int *p) { return *p; }"),
RoundtripTest::new("rt_cast_chain", "double f(int x) { return (double)(float)x; }"),
]
}
#[derive(Debug, Clone)]
pub struct DifferentialIRTest {
pub name: String,
pub source: String,
pub baseline_flags: Vec<String>,
pub compare_flags: Vec<String>,
pub allow_different_ir: bool,
}
impl DifferentialIRTest {
pub fn new(name: &str, source: &str) -> Self {
Self {
name: name.to_string(),
source: source.to_string(),
baseline_flags: vec!["-O0".to_string()],
compare_flags: vec!["-O2".to_string()],
allow_different_ir: true,
}
}
pub fn with_baseline(mut self, flags: Vec<&str>) -> Self {
self.baseline_flags = flags.into_iter().map(String::from).collect();
self
}
pub fn with_compare(mut self, flags: Vec<&str>) -> Self {
self.compare_flags = flags.into_iter().map(String::from).collect();
self
}
}
pub fn build_differential_tests() -> Vec<DifferentialIRTest> {
vec![
DifferentialIRTest::new(
"diff_O0_vs_O2_simple",
"int f(int a, int b) { return a + b; }",
)
.with_baseline(vec!["-O0"])
.with_compare(vec!["-O2"]),
DifferentialIRTest::new(
"diff_O0_vs_O2_struct",
"struct S { int a; int b; }; int f(struct S s) { return s.a + s.b; }",
)
.with_baseline(vec!["-O0"])
.with_compare(vec!["-O2"]),
DifferentialIRTest::new(
"diff_O0_vs_O2_loop",
"int f(int n) { int s = 0; for(int i=0;i<n;i++) s+=i; return s; }",
)
.with_baseline(vec!["-O0"])
.with_compare(vec!["-O2"]),
DifferentialIRTest::new(
"diff_O0_vs_Os",
"int f(int n) { if (n) return 1; return 0; }",
)
.with_baseline(vec!["-O0"])
.with_compare(vec!["-Os"]),
DifferentialIRTest::new(
"diff_sse_vs_sse2",
"#include <xmmintrin.h>\n__m128 f(__m128 a, __m128 b) { return _mm_add_ps(a, b); }",
)
.with_baseline(vec!["-msse"])
.with_compare(vec!["-msse2"]),
DifferentialIRTest::new("diff_32bit_vs_64bit", "long f(long x) { return x * 2; }")
.with_baseline(vec!["-m32"])
.with_compare(vec!["-m64"]),
DifferentialIRTest::new("diff_no_sanitize_vs_asan", "int f(int *p) { return *p; }")
.with_baseline(vec!["-O0"])
.with_compare(vec!["-O0", "-fsanitize=address"]),
DifferentialIRTest::new(
"diff_no_sanitize_vs_ubsan",
"int f(int x) { return x + 1; }",
)
.with_baseline(vec!["-O0"])
.with_compare(vec!["-O0", "-fsanitize=undefined"]),
DifferentialIRTest::new(
"diff_indirect_call",
"int f(int (*fn)(int), int x) { return fn(x); }",
)
.with_baseline(vec!["-O0"])
.with_compare(vec!["-O2"]),
DifferentialIRTest::new(
"diff_memset_vs_loop",
"void f(char *p, int n) { for (int i=0; i<n; i++) p[i]=0; }",
)
.with_baseline(vec!["-O0"])
.with_compare(vec!["-O2"]),
]
}
#[derive(Debug, Clone)]
pub struct RegressionTestCase {
pub id: String,
pub description: String,
pub source: String,
pub expected_ir: Vec<X86IRPattern>,
pub forbidden_ir: Vec<X86IRPattern>,
pub date_added: String,
pub issue_ref: Option<String>,
pub is_fixed: bool,
}
impl RegressionTestCase {
pub fn new(id: &str, desc: &str, source: &str) -> Self {
Self {
id: id.to_string(),
description: desc.to_string(),
source: source.to_string(),
expected_ir: Vec::new(),
forbidden_ir: Vec::new(),
date_added: String::new(),
issue_ref: None,
is_fixed: false,
}
}
pub fn with_expected(mut self, desc: &str, pat: &str) -> Self {
self.expected_ir.push(X86IRPattern::required(desc, pat));
self
}
pub fn with_forbidden(mut self, desc: &str, pat: &str) -> Self {
self.forbidden_ir.push(X86IRPattern::forbidden(desc, pat));
self
}
pub fn with_date(mut self, date: &str) -> Self {
self.date_added = date.to_string();
self
}
pub fn with_issue(mut self, issue: &str) -> Self {
self.issue_ref = Some(issue.to_string());
self
}
pub fn fixed(mut self) -> Self {
self.is_fixed = true;
self
}
}
#[derive(Debug, Clone, Default)]
pub struct RegressionRegistry {
pub tests: Vec<RegressionTestCase>,
}
impl RegressionRegistry {
pub fn new() -> Self {
Self { tests: Vec::new() }
}
pub fn register(&mut self, test: RegressionTestCase) {
self.tests.push(test);
}
pub fn count(&self) -> usize {
self.tests.len()
}
pub fn fixed_count(&self) -> usize {
self.tests.iter().filter(|t| t.is_fixed).count()
}
pub fn open_count(&self) -> usize {
self.tests.iter().filter(|t| !t.is_fixed).count()
}
pub fn by_id(&self, id: &str) -> Option<&RegressionTestCase> {
self.tests.iter().find(|t| t.id == id)
}
}
pub fn build_regression_registry() -> RegressionRegistry {
let mut registry = RegressionRegistry::new();
registry.register(RegressionTestCase::new(
"REG-001", "Struct field offset computation for packed structs",
"struct __attribute__((packed)) S { char a; int b; }; int f(struct S *s) { return s->b; }"
).with_expected("GEP for packed struct field", "getelementptr")
.with_date("2025-01-15")
.with_issue("GH-1234"));
registry.register(
RegressionTestCase::new(
"REG-002",
"VLA sizeof computation",
"#include <stddef.h>\nsize_t f(int n) { int arr[n]; return sizeof(arr); }",
)
.with_expected("VLA sizeof", "alloca")
.with_date("2025-02-10")
.with_issue("GH-1267"),
);
registry.register(
RegressionTestCase::new(
"REG-003",
"Long double return on x86",
"long double f(void) { return 3.14159265358979323846L; }",
)
.with_expected("x86_fp80 type for long double", "x86_fp80")
.with_date("2025-03-05")
.with_issue("GH-1298")
.fixed(),
);
registry.register(
RegressionTestCase::new(
"REG-004",
"Bitfield sign extension",
"struct S { signed int a: 3; }; int f(struct S s) { return s.a; }",
)
.with_expected("sign extension for signed bitfield", "sext")
.with_date("2025-04-01")
.with_issue("GH-1342")
.fixed(),
);
registry.register(RegressionTestCase::new(
"REG-005", "Atomic cmpxchg weak lowering",
"#include <stdatomic.h>\nint f(_Atomic int *p, int e, int d) { atomic_compare_exchange_weak(p, &e, d); return e; }"
).with_expected("cmpxchg weak", "cmpxchg weak")
.with_date("2025-05-15")
.with_issue("GH-1399"));
registry.register(RegressionTestCase::new(
"REG-006", "Nested lambda capture",
"int f(int x) { auto outer = [x](int y) { return [x, y](int z) { return x + y + z; }; }; return outer(2)(3); }"
).with_expected("nested lambda", "call")
.with_date("2025-06-01")
.with_issue("GH-1450"));
registry.register(
RegressionTestCase::new(
"REG-007",
"Flexible array member offset",
"struct S { int n; char data[]; }; size_t f(void) { return offsetof(struct S, data); }",
)
.with_expected("offsetof for flex array", "ret i64 4")
.with_date("2025-06-20")
.with_issue("GH-1488")
.fixed(),
);
registry.register(RegressionTestCase::new(
"REG-008", "Inline asm with multiple outputs",
"void f(int *a, int *b) { __asm__(\"mov %1, %0\" : \"=r\"(*a), \"=r\"(*b) : \"r\"(*a)); }"
).with_expected("multiple asm outputs", "call void asm")
.with_date("2025-07-10")
.with_issue("GH-1520"));
registry.register(
RegressionTestCase::new(
"REG-009",
"Static local with non-trivial dtor",
"struct S { ~S() {} }; S *f() { static S s; return &s; }",
)
.with_expected("guard variable", "guard")
.with_date("2025-08-05")
.with_issue("GH-1567"),
);
registry.register(RegressionTestCase::new(
"REG-010", "Vector shuffle for SSE psrldq",
"typedef char v16qi __attribute__((vector_size(16))); v16qi f(v16qi a) { return __builtin_shufflevector(a, a, 4,5,6,7,8,9,10,11,12,13,14,15,-1,-1,-1,-1); }"
).with_expected("shufflevector with undef", "shufflevector")
.with_date("2025-09-01")
.with_issue("GH-1600"));
registry
}
#[derive(Debug, Clone)]
pub struct IRDeltaDebugger {
pub original: String,
pub current: String,
pub iterations: usize,
pub granularity: usize,
}
impl IRDeltaDebugger {
pub fn new(source: &str) -> Self {
Self {
original: source.to_string(),
current: source.to_string(),
iterations: 0,
granularity: 2,
}
}
pub fn with_granularity(mut self, n: usize) -> Self {
self.granularity = n.max(2);
self
}
pub fn minimize_lines<F>(&mut self, is_interesting: F) -> &str
where
F: Fn(&str) -> bool,
{
let lines: Vec<&str> = self.current.lines().collect();
let total = lines.len();
if total == 0 {
return &self.current;
}
let mut chunk_size = total / self.granularity;
if chunk_size == 0 {
chunk_size = 1;
}
while chunk_size > 0 {
let mut i = 0;
while i + chunk_size <= lines.len() {
self.iterations += 1;
let reduced: Vec<&str> = lines[..i]
.iter()
.chain(lines[i + chunk_size..].iter())
.copied()
.collect();
let candidate = reduced.join("\n");
if is_interesting(&candidate) {
self.current = candidate;
return self.minimize_lines(is_interesting);
}
i += chunk_size;
}
chunk_size /= 2;
}
&self.current
}
pub fn minimize_chars<F>(&mut self, is_interesting: F) -> &str
where
F: Fn(&str) -> bool,
{
let chars: Vec<char> = self.current.chars().collect();
let total = chars.len();
if total == 0 {
return &self.current;
}
let mut chunk_size = total / self.granularity;
if chunk_size == 0 {
chunk_size = 1;
}
while chunk_size > 0 {
let mut i = 0;
while i + chunk_size <= chars.len() {
self.iterations += 1;
let reduced: String = chars[..i]
.iter()
.chain(chars[i + chunk_size..].iter())
.collect();
if is_interesting(&reduced) {
self.current = reduced;
return self.minimize_chars(is_interesting);
}
i += chunk_size;
}
chunk_size /= 2;
}
&self.current
}
pub fn stats(&self) -> String {
format!(
"Original: {} bytes → Reduced: {} bytes ({:.1}% reduction, {} iterations)",
self.original.len(),
self.current.len(),
(1.0 - self.current.len() as f64 / self.original.len().max(1) as f64) * 100.0,
self.iterations
)
}
pub fn reduction_ratio(&self) -> f64 {
if self.original.is_empty() {
return 0.0;
}
self.current.len() as f64 / self.original.len() as f64
}
pub fn reset(&mut self) {
self.current = self.original.clone();
self.iterations = 0;
}
}
#[derive(Debug, Clone)]
pub struct X86IntrinsicMapping {
pub c_name: String,
pub llvm_intrinsic: String,
pub isa_extension: String,
pub header: String,
pub description: String,
}
impl X86IntrinsicMapping {
pub fn new(c_name: &str, llvm: &str, isa: &str, header: &str, desc: &str) -> Self {
Self {
c_name: c_name.to_string(),
llvm_intrinsic: llvm.to_string(),
isa_extension: isa.to_string(),
header: header.to_string(),
description: desc.to_string(),
}
}
}
pub fn x86_intrinsic_catalog() -> Vec<X86IntrinsicMapping> {
vec![
X86IntrinsicMapping::new(
"_mm_add_ps",
"llvm.x86.sse.add.ps",
"SSE",
"xmmintrin.h",
"Packed single-precision floating-point addition",
),
X86IntrinsicMapping::new(
"_mm_sub_ps",
"llvm.x86.sse.sub.ps",
"SSE",
"xmmintrin.h",
"Packed single-precision floating-point subtraction",
),
X86IntrinsicMapping::new(
"_mm_mul_ps",
"llvm.x86.sse.mul.ps",
"SSE",
"xmmintrin.h",
"Packed single-precision floating-point multiplication",
),
X86IntrinsicMapping::new(
"_mm_div_ps",
"llvm.x86.sse.div.ps",
"SSE",
"xmmintrin.h",
"Packed single-precision floating-point division",
),
X86IntrinsicMapping::new(
"_mm_sqrt_ps",
"llvm.x86.sse.sqrt.ps",
"SSE",
"xmmintrin.h",
"Packed single-precision floating-point square root",
),
X86IntrinsicMapping::new(
"_mm_rcp_ps",
"llvm.x86.sse.rcp.ps",
"SSE",
"xmmintrin.h",
"Packed single-precision floating-point reciprocal",
),
X86IntrinsicMapping::new(
"_mm_rsqrt_ps",
"llvm.x86.sse.rsqrt.ps",
"SSE",
"xmmintrin.h",
"Packed single-precision floating-point reciprocal sqrt",
),
X86IntrinsicMapping::new(
"_mm_min_ps",
"llvm.x86.sse.min.ps",
"SSE",
"xmmintrin.h",
"Packed single-precision floating-point minimum",
),
X86IntrinsicMapping::new(
"_mm_max_ps",
"llvm.x86.sse.max.ps",
"SSE",
"xmmintrin.h",
"Packed single-precision floating-point maximum",
),
X86IntrinsicMapping::new(
"_mm_and_ps",
"llvm.x86.sse.and.ps",
"SSE",
"xmmintrin.h",
"Bitwise AND of packed single-precision values",
),
X86IntrinsicMapping::new(
"_mm_or_ps",
"llvm.x86.sse.or.ps",
"SSE",
"xmmintrin.h",
"Bitwise OR of packed single-precision values",
),
X86IntrinsicMapping::new(
"_mm_xor_ps",
"llvm.x86.sse.xor.ps",
"SSE",
"xmmintrin.h",
"Bitwise XOR of packed single-precision values",
),
X86IntrinsicMapping::new(
"_mm_cmpeq_ps",
"llvm.x86.sse.cmp.ps",
"SSE",
"xmmintrin.h",
"Compare packed single-precision for equality",
),
X86IntrinsicMapping::new(
"_mm_cmplt_ps",
"llvm.x86.sse.cmp.ps",
"SSE",
"xmmintrin.h",
"Compare packed single-precision for less-than",
),
X86IntrinsicMapping::new(
"_mm_cmple_ps",
"llvm.x86.sse.cmp.ps",
"SSE",
"xmmintrin.h",
"Compare packed single-precision for less-or-equal",
),
X86IntrinsicMapping::new(
"_mm_add_pd",
"llvm.x86.sse2.add.pd",
"SSE2",
"emmintrin.h",
"Packed double-precision floating-point addition",
),
X86IntrinsicMapping::new(
"_mm_sub_pd",
"llvm.x86.sse2.sub.pd",
"SSE2",
"emmintrin.h",
"Packed double-precision floating-point subtraction",
),
X86IntrinsicMapping::new(
"_mm_mul_pd",
"llvm.x86.sse2.mul.pd",
"SSE2",
"emmintrin.h",
"Packed double-precision floating-point multiplication",
),
X86IntrinsicMapping::new(
"_mm_div_pd",
"llvm.x86.sse2.div.pd",
"SSE2",
"emmintrin.h",
"Packed double-precision floating-point division",
),
X86IntrinsicMapping::new(
"_mm_sqrt_pd",
"llvm.x86.sse2.sqrt.pd",
"SSE2",
"emmintrin.h",
"Packed double-precision floating-point square root",
),
X86IntrinsicMapping::new(
"_mm_add_epi32",
"llvm.x86.sse2.padd.d",
"SSE2",
"emmintrin.h",
"Add packed 32-bit integers",
),
X86IntrinsicMapping::new(
"_mm_sub_epi32",
"llvm.x86.sse2.psub.d",
"SSE2",
"emmintrin.h",
"Subtract packed 32-bit integers",
),
X86IntrinsicMapping::new(
"_mm_mul_epu32",
"llvm.x86.sse2.pmulu.dq",
"SSE2",
"emmintrin.h",
"Multiply unsigned 32-bit integers and store 64-bit results",
),
X86IntrinsicMapping::new(
"_mm_slli_epi32",
"llvm.x86.sse2.pslli.d",
"SSE2",
"emmintrin.h",
"Shift packed 32-bit integers left logical",
),
X86IntrinsicMapping::new(
"_mm_srli_epi32",
"llvm.x86.sse2.psrli.d",
"SSE2",
"emmintrin.h",
"Shift packed 32-bit integers right logical",
),
X86IntrinsicMapping::new(
"_mm_srai_epi32",
"llvm.x86.sse2.psrai.d",
"SSE2",
"emmintrin.h",
"Shift packed 32-bit integers right arithmetic",
),
X86IntrinsicMapping::new(
"_mm_and_si128",
"llvm.x86.sse2.pand",
"SSE2",
"emmintrin.h",
"Bitwise AND of 128-bit integer vectors",
),
X86IntrinsicMapping::new(
"_mm_or_si128",
"llvm.x86.sse2.por",
"SSE2",
"emmintrin.h",
"Bitwise OR of 128-bit integer vectors",
),
X86IntrinsicMapping::new(
"_mm_xor_si128",
"llvm.x86.sse2.pxor",
"SSE2",
"emmintrin.h",
"Bitwise XOR of 128-bit integer vectors",
),
X86IntrinsicMapping::new(
"_mm_packs_epi32",
"llvm.x86.sse2.packssdw.128",
"SSE2",
"emmintrin.h",
"Pack signed 32-bit integers to 16-bit with saturation",
),
X86IntrinsicMapping::new(
"_mm_packus_epi32",
"llvm.x86.sse2.packusdw.128",
"SSE2",
"emmintrin.h",
"Pack unsigned 32-bit integers to 16-bit with saturation",
),
X86IntrinsicMapping::new(
"_mm_unpacklo_epi32",
"llvm.x86.sse2.punpckldq",
"SSE2",
"emmintrin.h",
"Unpack and interleave low 32-bit integers",
),
X86IntrinsicMapping::new(
"_mm_unpackhi_epi32",
"llvm.x86.sse2.punpckhdq",
"SSE2",
"emmintrin.h",
"Unpack and interleave high 32-bit integers",
),
X86IntrinsicMapping::new(
"_mm_hadd_ps",
"llvm.x86.sse3.hadd.ps",
"SSE3",
"pmmintrin.h",
"Horizontal add packed single-precision values",
),
X86IntrinsicMapping::new(
"_mm_hsub_ps",
"llvm.x86.sse3.hsub.ps",
"SSE3",
"pmmintrin.h",
"Horizontal subtract packed single-precision values",
),
X86IntrinsicMapping::new(
"_mm_addsub_ps",
"llvm.x86.sse3.addsub.ps",
"SSE3",
"pmmintrin.h",
"Alternating add/subtract packed single-precision values",
),
X86IntrinsicMapping::new(
"_mm_abs_epi32",
"llvm.x86.ssse3.pabs.d.128",
"SSSE3",
"tmmintrin.h",
"Absolute value of packed 32-bit integers",
),
X86IntrinsicMapping::new(
"_mm_shuffle_epi8",
"llvm.x86.ssse3.pshuf.b.128",
"SSSE3",
"tmmintrin.h",
"Shuffle packed 8-bit integers",
),
X86IntrinsicMapping::new(
"_mm_alignr_epi8",
"llvm.x86.ssse3.palign.r.128",
"SSSE3",
"tmmintrin.h",
"Concatenate and shift right packed 8-bit integers",
),
X86IntrinsicMapping::new(
"_mm_hadd_epi32",
"llvm.x86.ssse3.phadd.d.128",
"SSSE3",
"tmmintrin.h",
"Horizontal add packed 32-bit integers",
),
X86IntrinsicMapping::new(
"_mm_blend_ps",
"llvm.x86.sse41.blendps",
"SSE4.1",
"smmintrin.h",
"Blend packed single-precision values",
),
X86IntrinsicMapping::new(
"_mm_blend_pd",
"llvm.x86.sse41.blendpd",
"SSE4.1",
"smmintrin.h",
"Blend packed double-precision values",
),
X86IntrinsicMapping::new(
"_mm_dp_ps",
"llvm.x86.sse41.dpps",
"SSE4.1",
"smmintrin.h",
"Dot product of packed single-precision values",
),
X86IntrinsicMapping::new(
"_mm_round_ps",
"llvm.x86.sse41.round.ps",
"SSE4.1",
"smmintrin.h",
"Round packed single-precision values",
),
X86IntrinsicMapping::new(
"_mm_round_pd",
"llvm.x86.sse41.round.pd",
"SSE4.1",
"smmintrin.h",
"Round packed double-precision values",
),
X86IntrinsicMapping::new(
"_mm_min_epi32",
"llvm.x86.sse41.pminsd",
"SSE4.1",
"smmintrin.h",
"Minimum of packed signed 32-bit integers",
),
X86IntrinsicMapping::new(
"_mm_max_epi32",
"llvm.x86.sse41.pmaxsd",
"SSE4.1",
"smmintrin.h",
"Maximum of packed signed 32-bit integers",
),
X86IntrinsicMapping::new(
"_mm_mullo_epi32",
"llvm.x86.sse41.pmuldq",
"SSE4.1",
"smmintrin.h",
"Multiply packed 32-bit integers, store low 32 bits",
),
X86IntrinsicMapping::new(
"_mm_crc32_u8",
"llvm.x86.sse42.crc32.32.8",
"SSE4.2",
"nmmintrin.h",
"Accumulate CRC32 on unsigned 8-bit integer",
),
X86IntrinsicMapping::new(
"_mm_crc32_u32",
"llvm.x86.sse42.crc32.32.32",
"SSE4.2",
"nmmintrin.h",
"Accumulate CRC32 on unsigned 32-bit integer",
),
X86IntrinsicMapping::new(
"_mm_cmpestra",
"llvm.x86.sse42.pcmpestrm128",
"SSE4.2",
"nmmintrin.h",
"Compare packed strings with explicit lengths",
),
X86IntrinsicMapping::new(
"_mm256_add_ps",
"llvm.x86.avx.add.ps.256",
"AVX",
"immintrin.h",
"256-bit packed single-precision floating-point addition",
),
X86IntrinsicMapping::new(
"_mm256_sub_ps",
"llvm.x86.avx.sub.ps.256",
"AVX",
"immintrin.h",
"256-bit packed single-precision floating-point subtraction",
),
X86IntrinsicMapping::new(
"_mm256_mul_ps",
"llvm.x86.avx.mul.ps.256",
"AVX",
"immintrin.h",
"256-bit packed single-precision floating-point multiplication",
),
X86IntrinsicMapping::new(
"_mm256_div_ps",
"llvm.x86.avx.div.ps.256",
"AVX",
"immintrin.h",
"256-bit packed single-precision floating-point division",
),
X86IntrinsicMapping::new(
"_mm256_add_pd",
"llvm.x86.avx.add.pd.256",
"AVX",
"immintrin.h",
"256-bit packed double-precision floating-point addition",
),
X86IntrinsicMapping::new(
"_mm256_sqrt_ps",
"llvm.x86.avx.sqrt.ps.256",
"AVX",
"immintrin.h",
"256-bit packed single-precision floating-point square root",
),
X86IntrinsicMapping::new(
"_mm256_hadd_ps",
"llvm.x86.avx.hadd.ps.256",
"AVX",
"immintrin.h",
"Horizontal add 256-bit packed single-precision values",
),
X86IntrinsicMapping::new(
"_mm256_blend_ps",
"llvm.x86.avx.blend.ps.256",
"AVX",
"immintrin.h",
"Blend 256-bit packed single-precision values",
),
X86IntrinsicMapping::new(
"_mm256_add_epi32",
"llvm.x86.avx2.padd.d",
"AVX2",
"immintrin.h",
"Add packed 32-bit integers using 256-bit registers",
),
X86IntrinsicMapping::new(
"_mm256_mullo_epi32",
"llvm.x86.avx2.pmul.dq",
"AVX2",
"immintrin.h",
"Multiply packed 32-bit integers using 256-bit registers",
),
X86IntrinsicMapping::new(
"_mm256_slli_epi32",
"llvm.x86.avx2.pslli.d",
"AVX2",
"immintrin.h",
"Shift left packed 32-bit integers using 256-bit registers",
),
X86IntrinsicMapping::new(
"_mm256_and_si256",
"llvm.x86.avx2.pand",
"AVX2",
"immintrin.h",
"Bitwise AND of 256-bit integer vectors",
),
X86IntrinsicMapping::new(
"_mm256_abs_epi32",
"llvm.x86.avx2.pabs.d",
"AVX2",
"immintrin.h",
"Absolute value of packed 32-bit integers using 256-bit registers",
),
X86IntrinsicMapping::new(
"_mm512_add_ps",
"llvm.x86.avx512.add.ps.512",
"AVX-512F",
"immintrin.h",
"512-bit packed single-precision floating-point addition",
),
X86IntrinsicMapping::new(
"_mm512_mul_ps",
"llvm.x86.avx512.mul.ps.512",
"AVX-512F",
"immintrin.h",
"512-bit packed single-precision floating-point multiplication",
),
X86IntrinsicMapping::new(
"_mm512_add_pd",
"llvm.x86.avx512.add.pd.512",
"AVX-512F",
"immintrin.h",
"512-bit packed double-precision floating-point addition",
),
X86IntrinsicMapping::new(
"_mm_fmadd_ps",
"llvm.x86.fma.vfmadd.ps",
"FMA",
"immintrin.h",
"Fused multiply-add packed single-precision",
),
X86IntrinsicMapping::new(
"_mm_fmadd_pd",
"llvm.x86.fma.vfmadd.pd",
"FMA",
"immintrin.h",
"Fused multiply-add packed double-precision",
),
X86IntrinsicMapping::new(
"_mm256_fmadd_ps",
"llvm.x86.fma.vfmadd.ps.256",
"FMA",
"immintrin.h",
"256-bit fused multiply-add packed single-precision",
),
X86IntrinsicMapping::new(
"__blsr_u32",
"llvm.x86.bmi.blsr.32",
"BMI",
"x86intrin.h",
"Reset lowest set bit",
),
X86IntrinsicMapping::new(
"__blsmsk_u32",
"llvm.x86.bmi.blsmsk.32",
"BMI",
"x86intrin.h",
"Get mask up to lowest set bit",
),
X86IntrinsicMapping::new(
"__blsi_u32",
"llvm.x86.bmi.blsi.32",
"BMI",
"x86intrin.h",
"Extract lowest set isolated bit",
),
X86IntrinsicMapping::new(
"__tzcnt_u32",
"llvm.x86.bmi.tzcnt.32",
"BMI",
"x86intrin.h",
"Count trailing zeros",
),
X86IntrinsicMapping::new(
"__lzcnt32",
"llvm.x86.lzcnt.32",
"LZCNT",
"x86intrin.h",
"Count leading zeros",
),
X86IntrinsicMapping::new(
"__popcntd",
"llvm.x86.popcnt.32",
"POPCNT",
"x86intrin.h",
"Population count of 32-bit integer",
),
X86IntrinsicMapping::new(
"__rdtsc",
"llvm.x86.rdtsc",
"x86",
"x86intrin.h",
"Read Time-Stamp Counter",
),
X86IntrinsicMapping::new(
"__rdtscp",
"llvm.x86.rdtscp",
"x86",
"x86intrin.h",
"Read Time-Stamp Counter and Processor ID",
),
]
}
#[derive(Debug, Clone)]
pub struct EdgeCaseConfig {
pub include_overflow: bool,
pub include_div_zero: bool,
pub include_null_ptr: bool,
pub include_uninit: bool,
pub include_type_pun: bool,
pub include_large_stack: bool,
}
impl Default for EdgeCaseConfig {
fn default() -> Self {
Self {
include_overflow: true,
include_div_zero: true,
include_null_ptr: true,
include_uninit: true,
include_type_pun: true,
include_large_stack: true,
}
}
}
#[derive(Debug, Clone)]
pub struct EdgeCaseGenerator {
pub config: EdgeCaseConfig,
pub tests_generated: usize,
}
impl EdgeCaseGenerator {
pub fn new() -> Self {
Self {
config: EdgeCaseConfig::default(),
tests_generated: 0,
}
}
pub fn with_config(mut self, config: EdgeCaseConfig) -> Self {
self.config = config;
self
}
pub fn gen_overflow_tests(&self) -> Vec<X86IRGenTest> {
let mut tests = Vec::new();
if !self.config.include_overflow {
return tests;
}
tests.push(X86IRGenTest::new("edge_overflow_sadd", X86IRGenCategory::Stress,
"#include <stdbool.h>\nbool f(int a, int b) { int r; return __builtin_add_overflow(a, b, &r); }")
.require("sadd.with.overflow intrinsic", "llvm.sadd.with.overflow"));
tests.push(X86IRGenTest::new("edge_overflow_ssub", X86IRGenCategory::Stress,
"#include <stdbool.h>\nbool f(int a, int b) { int r; return __builtin_sub_overflow(a, b, &r); }")
.require("ssub.with.overflow intrinsic", "llvm.ssub.with.overflow"));
tests.push(X86IRGenTest::new("edge_overflow_smul", X86IRGenCategory::Stress,
"#include <stdbool.h>\nbool f(int a, int b) { int r; return __builtin_mul_overflow(a, b, &r); }")
.require("smul.with.overflow intrinsic", "llvm.smul.with.overflow"));
tests.push(X86IRGenTest::new("edge_overflow_uadd", X86IRGenCategory::Stress,
"#include <stdbool.h>\nbool f(unsigned a, unsigned b) { unsigned r; return __builtin_uadd_overflow(a, b, &r); }")
.require("uadd.with.overflow intrinsic", "llvm.uadd.with.overflow"));
tests
}
pub fn gen_div_zero_tests(&self) -> Vec<X86IRGenTest> {
let mut tests = Vec::new();
if !self.config.include_div_zero {
return tests;
}
tests.push(
X86IRGenTest::new(
"edge_div_zero_int",
X86IRGenCategory::Stress,
"int f(int a, int b) { return a / b; }",
)
.require("sdiv for division", "sdiv i32"),
);
tests.push(
X86IRGenTest::new(
"edge_div_zero_uint",
X86IRGenCategory::Stress,
"unsigned f(unsigned a, unsigned b) { return a / b; }",
)
.require("udiv for unsigned division", "udiv i32"),
);
tests.push(
X86IRGenTest::new(
"edge_mod_zero_int",
X86IRGenCategory::Stress,
"int f(int a, int b) { return a % b; }",
)
.require("srem for modulo", "srem i32"),
);
tests
}
pub fn gen_null_ptr_tests(&self) -> Vec<X86IRGenTest> {
let mut tests = Vec::new();
if !self.config.include_null_ptr {
return tests;
}
tests.push(
X86IRGenTest::new(
"edge_null_ptr_deref",
X86IRGenCategory::Stress,
"int f(int *p) { if (p) return *p; return 0; }",
)
.require("null check before deref", "icmp")
.require("conditional branch on null", "br i1"),
);
tests.push(
X86IRGenTest::new(
"edge_null_ptr_assign",
X86IRGenCategory::Stress,
"int *f(void) { return (int*)0; }",
)
.require("null pointer constant", "null"),
);
tests
}
pub fn gen_uninit_tests(&self) -> Vec<X86IRGenTest> {
let mut tests = Vec::new();
if !self.config.include_uninit {
return tests;
}
tests.push(
X86IRGenTest::new(
"edge_uninit_local",
X86IRGenCategory::Stress,
"int f(void) { int x; return x; }",
)
.with_flags(vec!["-Wno-uninitialized"])
.require("alloca for uninitialized local", "alloca"),
);
tests.push(
X86IRGenTest::new(
"edge_uninit_array",
X86IRGenCategory::Stress,
"int f(void) { int arr[10]; return arr[5]; }",
)
.with_flags(vec!["-Wno-uninitialized"])
.require("alloca for uninitialized array", "alloca"),
);
tests
}
pub fn gen_type_pun_tests(&self) -> Vec<X86IRGenTest> {
let mut tests = Vec::new();
if !self.config.include_type_pun {
return tests;
}
tests.push(
X86IRGenTest::new(
"edge_type_pun_union",
X86IRGenCategory::Stress,
"union U { float f; int i; }; int f(float x) { union U u; u.f = x; return u.i; }",
)
.require("union type punning", "load i32"),
);
tests.push(
X86IRGenTest::new(
"edge_type_pun_ptr_cast",
X86IRGenCategory::Stress,
"float f(int x) { return *(float*)&x; }",
)
.require("pointer cast for type punning", "bitcast"),
);
tests
}
pub fn gen_large_stack_tests(&self) -> Vec<X86IRGenTest> {
let mut tests = Vec::new();
if !self.config.include_large_stack {
return tests;
}
tests.push(
X86IRGenTest::new(
"edge_large_stack_array",
X86IRGenCategory::Stress,
"int f(void) { char buf[4096]; buf[0] = 'x'; return buf[0]; }",
)
.require("large stack alloca", "alloca [4096 x i8]"),
);
tests.push(
X86IRGenTest::new(
"edge_large_stack_vla",
X86IRGenCategory::Stress,
"int f(int n) { char buf[n + 1024]; return sizeof(buf); }",
)
.with_flags(vec!["-std=c99"])
.require("VLA with large size", "alloca"),
);
tests
}
pub fn generate_all(&mut self) -> Vec<X86IRGenTest> {
let mut tests = Vec::new();
tests.extend(self.gen_overflow_tests());
tests.extend(self.gen_div_zero_tests());
tests.extend(self.gen_null_ptr_tests());
tests.extend(self.gen_uninit_tests());
tests.extend(self.gen_type_pun_tests());
tests.extend(self.gen_large_stack_tests());
self.tests_generated = tests.len();
tests
}
}
impl Default for EdgeCaseGenerator {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct BatchVerificationResult {
pub group_name: String,
pub total: usize,
pub passed: usize,
pub failed: usize,
pub compile_errors: usize,
pub total_duration_ms: u64,
pub results: Vec<X86IRGenTestResult>,
pub category_results: BTreeMap<X86IRGenCategory, (usize, usize)>,
}
impl BatchVerificationResult {
pub fn new(group_name: &str, results: Vec<X86IRGenTestResult>) -> Self {
let total = results.len();
let passed = results.iter().filter(|r| r.passed).count();
let failed = total - passed;
let compile_errors = results.iter().filter(|r| !r.compiled).count();
let total_duration_ms = results.iter().map(|r| r.duration_ms).sum();
let category_results = BTreeMap::new();
Self {
group_name: group_name.to_string(),
total,
passed,
failed,
compile_errors,
total_duration_ms,
results,
category_results,
}
}
pub fn pass_rate(&self) -> f64 {
if self.total == 0 {
return 100.0;
}
self.passed as f64 / self.total as f64 * 100.0
}
pub fn is_perfect(&self) -> bool {
self.failed == 0
}
pub fn summary_report(&self) -> String {
let mut report = String::new();
report.push_str(&format!("Batch: {}\n", self.group_name));
report.push_str(&format!("════════════════════════════\n"));
report.push_str(&format!("Total: {:>6}\n", self.total));
report.push_str(&format!(
"Passed: {:>6} ({:.1}%)\n",
self.passed,
self.pass_rate()
));
report.push_str(&format!("Failed: {:>6}\n", self.failed));
report.push_str(&format!("Compile errors: {:>6}\n", self.compile_errors));
report.push_str(&format!(
"Duration: {:>6} ms\n",
self.total_duration_ms
));
if self.failed > 0 {
report.push_str("\nFailed tests:\n");
for r in &self.results {
if !r.passed {
report.push_str(&format!(" ✗ {}\n", r.name));
for check in r.failed_checks() {
report.push_str(&format!(" - {}\n", check));
}
}
}
}
report
}
pub fn count_by_category(&mut self, suite: &X86IRGenTestSuite) {
let mut cat_map: BTreeMap<X86IRGenCategory, (usize, usize)> = BTreeMap::new();
for result in &self.results {
if let Some(test) = suite.tests.iter().find(|t| t.name == result.name) {
let entry = cat_map.entry(test.category).or_insert((0, 0));
entry.0 += 1;
if result.passed {
entry.1 += 1;
}
}
}
self.category_results = cat_map;
}
}
#[derive(Debug, Clone, Default)]
pub struct IRComparator {
pub ignore_metadata: bool,
pub ignore_names: bool,
pub ignore_comments: bool,
pub ignore_debug_info: bool,
}
impl IRComparator {
pub fn new() -> Self {
Self::default()
}
pub fn ignore_metadata(mut self) -> Self {
self.ignore_metadata = true;
self
}
pub fn ignore_names(mut self) -> Self {
self.ignore_names = true;
self
}
pub fn similarity(&self, a: &str, b: &str) -> f64 {
let a_clean = self.clean(a);
let b_clean = self.clean(b);
let a_lines: Vec<&str> = a_clean.lines().filter(|l| !l.trim().is_empty()).collect();
let b_lines: Vec<&str> = b_clean.lines().filter(|l| !l.trim().is_empty()).collect();
if a_lines.is_empty() && b_lines.is_empty() {
return 1.0;
}
if a_lines.is_empty() || b_lines.is_empty() {
return 0.0;
}
let a_set: HashSet<&str> = a_lines.iter().copied().collect();
let b_set: HashSet<&str> = b_lines.iter().copied().collect();
let intersection = a_set.intersection(&b_set).count();
let union = a_set.union(&b_set).count();
if union == 0 {
return 1.0;
}
intersection as f64 / union as f64
}
pub fn same_function_count(&self, a: &str, b: &str) -> bool {
let count_a = a.matches("define ").count();
let count_b = b.matches("define ").count();
count_a == count_b
}
pub fn same_global_names(&self, a: &str, b: &str) -> bool {
let extract_globals = |text: &str| -> HashSet<&str> {
text.lines()
.filter(|l| l.trim().starts_with('@'))
.filter_map(|l| {
let trimmed = l.trim();
let name_end = trimmed.find('=').unwrap_or(trimmed.len());
Some(&trimmed[1..name_end.min(trimmed.len())])
})
.collect()
};
extract_globals(a) == extract_globals(b)
}
fn clean<'a>(&self, ir: &'a str) -> String {
let mut result = ir.to_string();
if self.ignore_metadata {
let re_lines: Vec<String> = result
.lines()
.filter(|l| !l.trim().starts_with('!'))
.map(|l| {
let mut cleaned = l.to_string();
while let Some(pos) = cleaned.find(", !") {
let rest = &cleaned[pos + 2..];
if let Some(space) = rest.find(|c: char| c.is_whitespace() || c == ')') {
cleaned.replace_range(pos..pos + 2 + space, "");
} else {
cleaned.truncate(pos);
break;
}
}
cleaned
})
.collect();
result = re_lines.join("\n");
}
if self.ignore_names {
let mut simplified = String::new();
let mut i = 0;
let chars: Vec<char> = result.chars().collect();
while i < chars.len() {
if chars[i] == '%' && i + 1 < chars.len() {
let start = i;
i += 1;
while i < chars.len()
&& (chars[i].is_alphanumeric() || chars[i] == '.' || chars[i] == '_')
{
i += 1;
}
let _name = &chars[start..i].iter().collect::<String>();
simplified.push_str("%v");
} else {
simplified.push(chars[i]);
i += 1;
}
}
result = simplified;
}
if self.ignore_comments {
result = result
.lines()
.filter(|l| !l.trim().starts_with(';'))
.map(|l| {
if let Some(pos) = l.find(';') {
l[..pos].to_string()
} else {
l.to_string()
}
})
.collect::<Vec<_>>()
.join("\n");
}
if self.ignore_debug_info {
result = result
.lines()
.filter(|l| {
let t = l.trim();
!t.contains("llvm.dbg.") && !t.contains("!dbg")
})
.collect::<Vec<_>>()
.join("\n");
}
result
}
}
pub fn build_c_language_tests() -> Vec<X86IRGenTest> {
vec![
X86IRGenTest::new("ir_c_global_int_init", X86IRGenCategory::CLanguage,
"int x = 42;\nint f(void) { return x; }")
.require("global with initializer", "@x")
.require("initializer value 42", "i32 42"),
X86IRGenTest::new("ir_c_global_zero_init", X86IRGenCategory::CLanguage,
"int x;\nint f(void) { return x; }")
.require("global tentative definition", "@x")
.require("zeroinitializer for BSS", "zeroinitializer"),
X86IRGenTest::new("ir_c_global_const", X86IRGenCategory::CLanguage,
"const int x = 100;\nint f(void) { return x; }")
.require("constant global", "constant"),
X86IRGenTest::new("ir_c_global_float_init", X86IRGenCategory::CLanguage,
"double pi = 3.14159;\ndouble f(void) { return pi; }")
.require("float global", "double")
.require("global variable @pi", "@pi"),
X86IRGenTest::new("ir_c_global_array_init", X86IRGenCategory::CLanguage,
"int arr[5] = {1, 2, 3, 4, 5};\nint f(int i) { return arr[i]; }")
.require("global array initializer", "@arr"),
X86IRGenTest::new("ir_c_global_string", X86IRGenCategory::CLanguage,
"const char *f(void) { return \"hello\"; }")
.require("string constant in IR", "@"),
X86IRGenTest::new("ir_c_static_local", X86IRGenCategory::CLanguage,
"int f(void) { static int x = 0; return x++; }")
.require("static local variable", "internal"),
X86IRGenTest::new("ir_c_static_local_init", X86IRGenCategory::CLanguage,
"int f(void) { static int x = 42; x++; return x; }")
.require("static local with initializer", "internal"),
X86IRGenTest::new("ir_c_static_local_guard", X86IRGenCategory::CLanguage,
"int f(void) { static int x = g(); return x; }")
.require("guard variable for dynamic init", "guard"),
X86IRGenTest::new("ir_c_array_decay_param", X86IRGenCategory::CLanguage,
"int f(int arr[]) { return arr[0]; }")
.require("array parameter decays to pointer", "i32\\*"),
X86IRGenTest::new("ir_c_array_decay_assign", X86IRGenCategory::CLanguage,
"int f(void) { int arr[10]; int *p = arr; return p[0]; }")
.require("array decay in assignment", "getelementptr"),
X86IRGenTest::new("ir_c_array_decay_sizeof", X86IRGenCategory::CLanguage,
"#include <stddef.h>\nsize_t f(void) { int arr[10]; return sizeof(arr); }")
.require("sizeof on array not decayed", "40"),
X86IRGenTest::new("ir_c_func_decay", X86IRGenCategory::CLanguage,
"void g(void) {} void f(void) { void (*fp)(void) = g; fp(); }")
.require("function pointer value", "@g")
.require("indirect call", "call"),
X86IRGenTest::new("ir_c_implicit_int_promotion", X86IRGenCategory::CLanguage,
"int f(char c) { return c + 1; }")
.require("sign extension from i8 to i32", "sext"),
X86IRGenTest::new("ir_c_implicit_float_promotion", X86IRGenCategory::CLanguage,
"double f(float x) { return x + 1.0; }")
.require("float to double promotion", "fpext"),
X86IRGenTest::new("ir_c_implicit_int_to_float", X86IRGenCategory::CLanguage,
"float f(int x) { return x + 0.5f; }")
.require("int to float conversion", "sitofp"),
X86IRGenTest::new("ir_c_implicit_float_to_int", X86IRGenCategory::CLanguage,
"int f(float x) { return x; }")
.require("float to int conversion", "fptosi"),
X86IRGenTest::new("ir_c_implicit_truncate", X86IRGenCategory::CLanguage,
"char f(int x) { return x; }")
.require("i32 to i8 truncation", "trunc"),
X86IRGenTest::new("ir_c_implicit_ptr_cast", X86IRGenCategory::CLanguage,
"int f(void *p) { return *(int *)p; }")
.require("pointer cast", "bitcast i8\\* to i32\\*"),
X86IRGenTest::new("ir_c_compound_literal_int", X86IRGenCategory::CLanguage,
"int f(void) { int *p = (int[]){1, 2, 3}; return p[1]; }")
.require("compound literal alloca", "alloca"),
X86IRGenTest::new("ir_c_compound_literal_struct", X86IRGenCategory::CLanguage,
"struct Point { int x, y; }; int f(void) { struct Point p = (struct Point){10, 20}; return p.x; }")
.require("compound literal struct", "struct.Point"),
X86IRGenTest::new("ir_c_compound_literal_global", X86IRGenCategory::CLanguage,
"struct S { int a; double b; }; struct S *f(void) { return &(struct S){1, 2.0}; }")
.require("compound literal of struct", "alloca"),
X86IRGenTest::new("ir_c_designated_init_struct", X86IRGenCategory::CLanguage,
"struct S { int a; double b; char c; }; struct S f(void) { struct S s = {.b = 3.14, .c = 'x'}; return s; }")
.require("designated initializer for struct", "insertvalue"),
X86IRGenTest::new("ir_c_designated_init_array", X86IRGenCategory::CLanguage,
"int f(void) { int arr[5] = {[2] = 42, [4] = 99}; return arr[2]; }")
.require("designated initializer for array", "i32 42"),
X86IRGenTest::new("ir_c_vla_simple", X86IRGenCategory::CLanguage,
"int f(int n) { int arr[n]; return sizeof(arr); }")
.with_flags(vec!["-std=c99"])
.require("VLA alloca", "alloca"),
X86IRGenTest::new("ir_c_vla_multi_dim", X86IRGenCategory::CLanguage,
"int f(int n, int m) { int arr[n][m]; return sizeof(arr); }")
.with_flags(vec!["-std=c99"])
.require("multi-dim VLA", "alloca"),
X86IRGenTest::new("ir_c_flex_array", X86IRGenCategory::CLanguage,
"struct S { int n; char data[]; }; int f(struct S *s) { return s->data[0]; }")
.require("flexible array member access", "getelementptr"),
X86IRGenTest::new("ir_c_flex_array_zero", X86IRGenCategory::CLanguage,
"struct S { int n; char data[0]; }; int f(struct S *s) { return s->n; }")
.require("zero-length array", "getelementptr"),
X86IRGenTest::new("ir_c_function_local_static_guard", X86IRGenCategory::CLanguage,
"int f(void) { static int x = 42; return x++; }")
.require("static local function variable", "internal")
.forbid("no guard needed for constant init", "guard"),
X86IRGenTest::new("ir_c_tentative_def", X86IRGenCategory::CLanguage,
"int x; int x = 42; int f(void) { return x; }")
.require("tentative definition resolution", "i32 42"),
X86IRGenTest::new("ir_c_extern_var", X86IRGenCategory::CLanguage,
"extern int x; int f(void) { return x; }")
.require("extern reference", "@x")
.require("external linkage", "external"),
X86IRGenTest::new("ir_c_static_func", X86IRGenCategory::CLanguage,
"static int helper(int x) { return x * 2; } int f(int a) { return helper(a); }")
.require("internal linkage for static func", "internal")
.require("call to static helper", "call i32 @helper"),
X86IRGenTest::new("ir_c_inline_func", X86IRGenCategory::CLanguage,
"inline int add(int a, int b) { return a + b; } int f(int x, int y) { return add(x, y); }")
.require("inline function has linkonce_odr", "linkonce_odr"),
X86IRGenTest::new("ir_c_aligned_var", X86IRGenCategory::CLanguage,
"int x __attribute__((aligned(64))) = 0; int f(void) { return x; }")
.require("aligned global variable", "align 64"),
X86IRGenTest::new("ir_c_section_attr", X86IRGenCategory::CLanguage,
"int x __attribute__((section(\".mysection\"))) = 42;")
.require("section attribute on global", "mysection"),
X86IRGenTest::new("ir_c_aligned_local", X86IRGenCategory::CLanguage,
"int f(void) { int x __attribute__((aligned(32))) = 0; return x; }")
.require("aligned alloca", "align 32"),
X86IRGenTest::new("ir_c_restrict_ptr", X86IRGenCategory::CLanguage,
"void f(int *restrict p, int *restrict q) { *p = *q; }")
.require("noalias on restrict pointer", "noalias"),
X86IRGenTest::new("ir_c_noreturn_specifier", X86IRGenCategory::CLanguage,
"_Noreturn void f(void) { while(1); }")
.require("noreturn attribute on function", "noreturn"),
X86IRGenTest::new("ir_c_static_assert", X86IRGenCategory::CLanguage,
"_Static_assert(sizeof(int) == 4, \"int must be 4 bytes\"); int f(void) { return 0; }")
.require("static assert does not affect IR", "define"),
X86IRGenTest::new("ir_c_generic", X86IRGenCategory::CLanguage,
"#include <math.h>\ndouble f(double x) { return _Generic(x, float: sqrtf, double: sqrt)(x); }")
.require("_Generic resolves at compile time", "call"),
X86IRGenTest::new("ir_c_string_concat", X86IRGenCategory::CLanguage,
"const char *f(void) { return \"hello\" \" world\"; }")
.require("concatenated string literal", "hello world"),
X86IRGenTest::new("ir_c_wide_string", X86IRGenCategory::CLanguage,
"const wchar_t *f(void) { return L\"hello\"; }")
.require("wide string literal", "i32"),
X86IRGenTest::new("ir_c_utf8_string", X86IRGenCategory::CLanguage,
"const char *f(void) { return u8\"hello\"; }")
.require("UTF-8 string literal", "i8"),
X86IRGenTest::new("ir_c_array_from_string", X86IRGenCategory::CLanguage,
"char f(void) { char s[] = \"hi\"; return s[0]; }")
.require("array initialized from string", "alloca"),
]
}
pub fn build_cxx_tests() -> Vec<X86IRGenTest> {
vec![
X86IRGenTest::new("ir_cxx_vtable_layout", X86IRGenCategory::CPlusPlus,
"struct Base { virtual int f() { return 1; } virtual int g() { return 2; } }; int caller(Base *b) { return b->f(); }")
.require("vtable global", "@_ZTV")
.require("virtual function call via vtable", "getelementptr"),
X86IRGenTest::new("ir_cxx_vtable_single_inherit", X86IRGenCategory::CPlusPlus,
"struct A { virtual void f() {} }; struct B : A { virtual void f() override {} }; void test(B *b) { b->f(); }")
.require("vtable for derived class", "@_ZTV"),
X86IRGenTest::new("ir_cxx_vtable_multiple", X86IRGenCategory::CPlusPlus,
"struct A { virtual void fa() {} }; struct B { virtual void fb() {} }; struct C : A, B { virtual void fa() override {} virtual void fb() override {} }; void test(C *c) { c->fa(); }")
.require("multiple inheritance vtable", "@_ZTV"),
X86IRGenTest::new("ir_cxx_vtable_thunk", X86IRGenCategory::CPlusPlus,
"struct A { virtual void f() {} }; struct B : A { virtual void f() override {} }; void test(B *b) { b->f(); }")
.require("vtable entry for virtual function", "@_ZTV"),
X86IRGenTest::new("ir_cxx_rtti_descriptor", X86IRGenCategory::CPlusPlus,
"struct S { virtual void f() {} }; const char *f() { return typeid(S).name(); }")
.require("RTTI type info", "@_ZTI"),
X86IRGenTest::new("ir_cxx_rtti_name", X86IRGenCategory::CPlusPlus,
"struct Foo { virtual ~Foo() {} }; const char *f() { return typeid(Foo).name(); }")
.require("typeinfo name", "@_ZTS"),
X86IRGenTest::new("ir_cxx_landingpad", X86IRGenCategory::ExceptionHandling,
"void f(void) { try { throw 42; } catch (int) {} }")
.require("landingpad instruction", "landingpad"),
X86IRGenTest::new("ir_cxx_invoke", X86IRGenCategory::ExceptionHandling,
"void may_throw(); void f(void) { try { may_throw(); } catch (...) {} }")
.require("invoke instruction", "invoke void @"),
X86IRGenTest::new("ir_cxx_resume", X86IRGenCategory::ExceptionHandling,
"void g(void); void f(void) { try { try { g(); } catch(int) { throw; } } catch(int) {} }")
.require("resume instruction for rethrow", "resume"),
X86IRGenTest::new("ir_cxx_eh_personality", X86IRGenCategory::ExceptionHandling,
"void f(void) { try { throw 1; } catch (...) {} }")
.require("personality routine", "personality"),
X86IRGenTest::new("ir_cxx_eh_typeid", X86IRGenCategory::ExceptionHandling,
"void f(void) { try { throw 42; } catch (int) {} catch (double) {} }")
.require("catch clauses", "catch"),
X86IRGenTest::new("ir_cxx_eh_cleanup", X86IRGenCategory::ExceptionHandling,
"struct S { ~S() {} }; void f(void) { S s; throw 42; }")
.require("cleanup in landingpad", "cleanup"),
X86IRGenTest::new("ir_cxx_eh_filter", X86IRGenCategory::ExceptionHandling,
"void f(void) { try { throw 1; } catch (int) {} }")
.require("filter for catch", "filter"),
X86IRGenTest::new("ir_cxx_ctor_call", X86IRGenCategory::CPlusPlus,
"struct S { S() {} int x; }; S f(void) { S s; return s; }")
.require("constructor call", "call void @"),
X86IRGenTest::new("ir_cxx_dtor_call", X86IRGenCategory::CPlusPlus,
"struct S { ~S() {} }; void f(void) { S s; }")
.require("destructor call", "call void @"),
X86IRGenTest::new("ir_cxx_ctor_init_list", X86IRGenCategory::CPlusPlus,
"struct A { int x; A(int a) : x(a) {} }; A f(void) { return A(42); }")
.require("constructor with init list", "call"),
X86IRGenTest::new("ir_cxx_copy_ctor", X86IRGenCategory::CPlusPlus,
"struct S { S() {} S(const S&) {} }; S f(S x) { return x; }")
.require("copy constructor", "call"),
X86IRGenTest::new("ir_cxx_member_ptr_data", X86IRGenCategory::CPlusPlus,
"struct S { int x; }; int f(S s, int S::*mp) { return s.*mp; }")
.require("member pointer", "i32"),
X86IRGenTest::new("ir_cxx_member_ptr_func", X86IRGenCategory::CPlusPlus,
"struct S { int f() { return 42; } }; int g(S *s, int (S::*mp)()) { return (s->*mp)(); }")
.require("member function pointer call", "call"),
X86IRGenTest::new("ir_cxx_lambda_empty", X86IRGenCategory::CPlusPlus,
"void f(void) { auto l = []() { return 42; }; l(); }")
.require("lambda closure", "operator()"),
X86IRGenTest::new("ir_cxx_lambda_capture_by_val", X86IRGenCategory::CPlusPlus,
"int f(int x) { auto l = [x]() { return x; }; return l(); }")
.require("lambda capture by value", "call"),
X86IRGenTest::new("ir_cxx_lambda_capture_by_ref", X86IRGenCategory::CPlusPlus,
"int f(int x) { auto l = [&x]() { x = 42; return x; }; return l(); }")
.require("lambda capture by reference", "call"),
X86IRGenTest::new("ir_cxx_lambda_capture_mutable", X86IRGenCategory::CPlusPlus,
"int f(int x) { auto l = [x]() mutable { x++; return x; }; return l(); }")
.require("mutable lambda", "call"),
X86IRGenTest::new("ir_cxx_template_func", X86IRGenCategory::CPlusPlus,
"template<typename T> T add(T a, T b) { return a + b; } int f(void) { return add(1, 2); }")
.require("template function instantiation", "add"),
X86IRGenTest::new("ir_cxx_template_class", X86IRGenCategory::CPlusPlus,
"template<typename T> struct Box { T val; T get() { return val; } }; int f(void) { Box<int> b; b.val = 42; return b.get(); }")
.require("template class instantiation", "call"),
X86IRGenTest::new("ir_cxx_template_specialization", X86IRGenCategory::CPlusPlus,
"template<typename T> T twice(T x) { return x + x; } template<> double twice<double>(double x) { return x * 2.0; } double f(void) { return twice(3.14); }")
.require("template specialization", "double"),
X86IRGenTest::new("ir_cxx_new", X86IRGenCategory::CPlusPlus,
"int *f(void) { return new int(42); }")
.require("new operator call", "call"),
X86IRGenTest::new("ir_cxx_new_array", X86IRGenCategory::CPlusPlus,
"int *f(int n) { return new int[n]; }")
.require("new array call", "call"),
X86IRGenTest::new("ir_cxx_delete", X86IRGenCategory::CPlusPlus,
"void f(int *p) { delete p; }")
.require("delete call", "call"),
X86IRGenTest::new("ir_cxx_delete_array", X86IRGenCategory::CPlusPlus,
"void f(int *p) { delete[] p; }")
.require("delete array call", "call"),
X86IRGenTest::new("ir_cxx_mangled_name", X86IRGenCategory::CPlusPlus,
"namespace ns { int f(int x) { return x; } }")
.require("name mangling for namespace function", "@_ZN"),
X86IRGenTest::new("ir_cxx_mangled_overload", X86IRGenCategory::CPlusPlus,
"void f(int) {} void f(double) {}")
.require("overloaded function names mangled", "@_Z"),
X86IRGenTest::new("ir_cxx_static_member", X86IRGenCategory::CPlusPlus,
"struct S { static int x; }; int S::x = 0; int f(void) { return S::x; }")
.require("static member variable", "@"),
X86IRGenTest::new("ir_cxx_virtual_base", X86IRGenCategory::CPlusPlus,
"struct A { int x; }; struct B : virtual A { int y; }; int f(B *b) { return b->x; }")
.require("virtual base pointer access", "getelementptr"),
X86IRGenTest::new("ir_cxx_virtual_base_offset", X86IRGenCategory::CPlusPlus,
"struct A { int x; }; struct B : virtual A {}; struct C : B { int z; }; int f(C *c) { return c->x; }")
.require("virtual base offset in deeper hierarchy", "getelementptr"),
X86IRGenTest::new("ir_cxx_pure_virtual", X86IRGenCategory::CPlusPlus,
"struct A { virtual void f() = 0; }; void g(A *a) { a->f(); }")
.require("pure virtual in vtable", "@_ZTV")
.require("__cxa_pure_virtual entry", "pure_virtual"),
X86IRGenTest::new("ir_cxx_virtual_dtor", X86IRGenCategory::CPlusPlus,
"struct A { virtual ~A() {} }; A *f(void) { return new A(); }")
.require("virtual destructor in vtable", "@_ZTV")
.require("D0/D1 destructor variants", "call"),
X86IRGenTest::new("ir_cxx_dynamic_cast", X86IRGenCategory::CPlusPlus,
"struct A { virtual void f() {} }; struct B : A {}; B *f(A *a) { return dynamic_cast<B*>(a); }")
.require("dynamic_cast lowering", "__dynamic_cast"),
X86IRGenTest::new("ir_cxx_typeid", X86IRGenCategory::CPlusPlus,
"#include <typeinfo>\nconst std::type_info *f() { return &typeid(int); }")
.require("typeid lowering", "@_ZTI"),
X86IRGenTest::new("ir_cxx_const_method", X86IRGenCategory::CPlusPlus,
"struct S { int get() const { return 42; } }; int f(const S *s) { return s->get(); }")
.require("const method call", "call"),
X86IRGenTest::new("ir_cxx_volatile_method", X86IRGenCategory::CPlusPlus,
"struct S { int get() volatile { return 42; } }; int f(volatile S *s) { return s->get(); }")
.require("volatile method call", "call"),
X86IRGenTest::new("ir_cxx_explicit_instantiation", X86IRGenCategory::CPlusPlus,
"template<typename T> T min(T a, T b) { return a < b ? a : b; } template int min<int>(int, int);")
.require("explicit instantiation", "define"),
X86IRGenTest::new("ir_cxx_default_ctor", X86IRGenCategory::CPlusPlus,
"struct S { S() = default; int x = 42; }; int f() { S s; return s.x; }")
.require("default constructor", "call"),
X86IRGenTest::new("ir_cxx_default_dtor", X86IRGenCategory::CPlusPlus,
"struct S { ~S() = default; }; void f() { S s; }")
.require("default destructor call", "call"),
X86IRGenTest::new("ir_cxx_deleted_copy", X86IRGenCategory::CPlusPlus,
"struct S { S(const S&) = delete; S() {} }; S f() { return S(); }")
.require("deleted copy constructor not emitted", "define"),
X86IRGenTest::new("ir_cxx_operator_plus", X86IRGenCategory::CPlusPlus,
"struct Vec { int x, y; Vec operator+(const Vec &o) { return {x+o.x, y+o.y}; } }; Vec f(Vec a, Vec b) { return a + b; }")
.require("operator+ call", "call"),
X86IRGenTest::new("ir_cxx_operator_eq", X86IRGenCategory::CPlusPlus,
"struct S { int x; bool operator==(const S &o) { return x == o.x; } }; bool f(S a, S b) { return a == b; }")
.require("operator== call", "call"),
X86IRGenTest::new("ir_cxx_using_decl", X86IRGenCategory::CPlusPlus,
"namespace A { int f(int x) { return x; } } using A::f; int g(int a) { return f(a); }")
.require("using declaration resolves", "call i32 @"),
X86IRGenTest::new("ir_cxx_static_const_member", X86IRGenCategory::CPlusPlus,
"struct S { static const int val = 100; }; int f() { return S::val; }")
.require("static const member folded", "ret i32 100"),
X86IRGenTest::new("ir_cxx_aggregate_init", X86IRGenCategory::CPlusPlus,
"struct S { int a; double b; }; S f() { return {1, 2.5}; }")
.require("aggregate initialization", "insertvalue"),
X86IRGenTest::new("ir_cxx_structured_binding", X86IRGenCategory::CPlusPlus,
"struct S { int a; double b; }; int f(S s) { auto [x, y] = s; return x; }")
.with_flags(vec!["-std=c++17"])
.require("structured binding lowering", "extractvalue"),
X86IRGenTest::new("ir_cxx_if_init", X86IRGenCategory::CPlusPlus,
"int f(int x) { if (int y = x * 2; y > 10) return y; return 0; }")
.with_flags(vec!["-std=c++17"])
.require("if with init statement", "br i1"),
X86IRGenTest::new("ir_cxx_variadic_template", X86IRGenCategory::CPlusPlus,
"template<typename... Args> int sum(Args... args) { return (args + ...); } int f() { return sum(1, 2, 3); }")
.with_flags(vec!["-std=c++17"])
.require("variadic template instantiation", "add"),
X86IRGenTest::new("ir_cxx_constexpr_if", X86IRGenCategory::CPlusPlus,
"template<typename T> T twice(T x) { if constexpr (sizeof(T) < 8) return x + x; else return x * 2; } int f() { return twice(21); }")
.with_flags(vec!["-std=c++17"])
.require("constexpr if resolved at compile time", "add i32"),
X86IRGenTest::new("ir_cxx_explicit_dtor", X86IRGenCategory::CPlusPlus,
"struct S { ~S() {} }; void f(S *s) { s->~S(); }")
.require("explicit destructor call", "call void @"),
X86IRGenTest::new("ir_cxx_placement_new", X86IRGenCategory::CPlusPlus,
"#include <new>\nstruct S { int x; }; S *f(void *buf) { return new (buf) S(); }")
.require("placement new call", "call"),
X86IRGenTest::new("ir_cxx_alignof", X86IRGenCategory::CPlusPlus,
"#include <cstddef>\nsize_t f() { return alignof(double); }")
.require("C++ alignof", "ret i64 8"),
X86IRGenTest::new("ir_cxx_alignas", X86IRGenCategory::CPlusPlus,
"struct alignas(64) S { int x; }; int f() { return alignof(S); }")
.require("alignas struct", "64"),
]
}
pub fn build_x86_specific_tests() -> Vec<X86IRGenTest> {
vec![
X86IRGenTest::new("ir_x86_vector_v2i64", X86IRGenCategory::X86Specific,
"typedef long long v2di __attribute__((vector_size(16))); v2di f(v2di a, v2di b) { return a + b; }")
.require("<2 x i64> vector type", "<2 x i64>"),
X86IRGenTest::new("ir_x86_vector_v4i32", X86IRGenCategory::X86Specific,
"typedef int v4si __attribute__((vector_size(16))); v4si f(v4si a, v4si b) { return a + b; }")
.require("<4 x i32> vector type", "<4 x i32>"),
X86IRGenTest::new("ir_x86_vector_v8i16", X86IRGenCategory::X86Specific,
"typedef short v8hi __attribute__((vector_size(16))); v8hi f(v8hi a, v8hi b) { return a + b; }")
.require("<8 x i16> vector type", "<8 x i16>"),
X86IRGenTest::new("ir_x86_vector_v16i8", X86IRGenCategory::X86Specific,
"typedef char v16qi __attribute__((vector_size(16))); v16qi f(v16qi a, v16qi b) { return a + b; }")
.require("<16 x i8> vector type", "<16 x i8>"),
X86IRGenTest::new("ir_x86_vector_v2f64", X86IRGenCategory::X86Specific,
"typedef double v2df __attribute__((vector_size(16))); v2df f(v2df a, v2df b) { return a + b; }")
.require("<2 x double> vector type", "<2 x double>"),
X86IRGenTest::new("ir_x86_vector_v4f32", X86IRGenCategory::X86Specific,
"typedef float v4sf __attribute__((vector_size(16))); v4sf f(v4sf a, v4sf b) { return a + b; }")
.require("<4 x float> vector type", "<4 x float>"),
X86IRGenTest::new("ir_x86_vector_v8f32", X86IRGenCategory::X86Specific,
"typedef float v8sf __attribute__((vector_size(32))); v8sf f(v8sf a, v8sf b) { return a + b; }")
.require("<8 x float> vector type", "<8 x float>"),
X86IRGenTest::new("ir_x86_vector_v4f64", X86IRGenCategory::X86Specific,
"typedef double v4df __attribute__((vector_size(32))); v4df f(v4df a, v4df b) { return a + b; }")
.require("<4 x double> vector type", "<4 x double>"),
X86IRGenTest::new("ir_x86_sse_addps", X86IRGenCategory::X86Specific,
"#include <xmmintrin.h>\n__m128 f(__m128 a, __m128 b) { return _mm_add_ps(a, b); }")
.require("SSE addps intrinsic", "llvm.x86.sse"),
X86IRGenTest::new("ir_x86_sse_mulps", X86IRGenCategory::X86Specific,
"#include <xmmintrin.h>\n__m128 f(__m128 a, __m128 b) { return _mm_mul_ps(a, b); }")
.require("SSE mulps intrinsic", "llvm.x86.sse"),
X86IRGenTest::new("ir_x86_sse_sqrtps", X86IRGenCategory::X86Specific,
"#include <xmmintrin.h>\n__m128 f(__m128 a) { return _mm_sqrt_ps(a); }")
.require("SSE sqrtps intrinsic", "llvm.x86.sse.sqrt"),
X86IRGenTest::new("ir_x86_sse_movaps", X86IRGenCategory::X86Specific,
"#include <xmmintrin.h>\nvoid f(float *p, __m128 v) { _mm_store_ps(p, v); }")
.require("SSE store intrinsic", "llvm.x86.sse"),
X86IRGenTest::new("ir_x86_sse2_addpd", X86IRGenCategory::X86Specific,
"#include <emmintrin.h>\n__m128d f(__m128d a, __m128d b) { return _mm_add_pd(a, b); }")
.require("SSE2 addpd intrinsic", "llvm.x86.sse2"),
X86IRGenTest::new("ir_x86_sse2_paddd", X86IRGenCategory::X86Specific,
"#include <emmintrin.h>\n__m128i f(__m128i a, __m128i b) { return _mm_add_epi32(a, b); }")
.require("SSE2 paddd intrinsic", "llvm.x86.sse2"),
X86IRGenTest::new("ir_x86_sse3_haddps", X86IRGenCategory::X86Specific,
"#include <pmmintrin.h>\n__m128 f(__m128 a, __m128 b) { return _mm_hadd_ps(a, b); }")
.require("SSE3 haddps intrinsic", "llvm.x86.sse3"),
X86IRGenTest::new("ir_x86_ssse3_pshufb", X86IRGenCategory::X86Specific,
"#include <tmmintrin.h>\n__m128i f(__m128i a, __m128i b) { return _mm_shuffle_epi8(a, b); }")
.require("SSSE3 pshufb intrinsic", "llvm.x86.ssse3"),
X86IRGenTest::new("ir_x86_sse41_roundps", X86IRGenCategory::X86Specific,
"#include <smmintrin.h>\n__m128 f(__m128 a) { return _mm_round_ps(a, _MM_FROUND_TO_NEAREST_INT); }")
.require("SSE4.1 roundps intrinsic", "llvm.x86.sse41"),
X86IRGenTest::new("ir_x86_sse42_crc32", X86IRGenCategory::X86Specific,
"#include <nmmintrin.h>\nunsigned int f(unsigned int crc, unsigned char v) { return _mm_crc32_u8(crc, v); }")
.require("SSE4.2 crc32 intrinsic", "llvm.x86.sse42"),
X86IRGenTest::new("ir_x86_avx_addps256", X86IRGenCategory::X86Specific,
"#include <immintrin.h>\n__m256 f(__m256 a, __m256 b) { return _mm256_add_ps(a, b); }")
.require("AVX addps 256 intrinsic", "llvm.x86.avx"),
X86IRGenTest::new("ir_x86_avx_mulpd256", X86IRGenCategory::X86Specific,
"#include <immintrin.h>\n__m256d f(__m256d a, __m256d b) { return _mm256_mul_pd(a, b); }")
.require("AVX mulpd intrinsic", "llvm.x86.avx"),
X86IRGenTest::new("ir_x86_avx_haddps256", X86IRGenCategory::X86Specific,
"#include <immintrin.h>\n__m256 f(__m256 a, __m256 b) { return _mm256_hadd_ps(a, b); }")
.require("AVX haddps intrinsic", "llvm.x86.avx"),
X86IRGenTest::new("ir_x86_avx2_add_i32", X86IRGenCategory::X86Specific,
"#include <immintrin.h>\n__m256i f(__m256i a, __m256i b) { return _mm256_add_epi32(a, b); }")
.require("AVX2 integer add intrinsic", "llvm.x86.avx2"),
X86IRGenTest::new("ir_x86_avx512_addps512", X86IRGenCategory::X86Specific,
"#include <immintrin.h>\n__m512 f(__m512 a, __m512 b) { return _mm512_add_ps(a, b); }")
.with_flags(vec!["-mavx512f"])
.require("AVX-512 addps intrinsic", "llvm.x86.avx512"),
X86IRGenTest::new("ir_x86_avx512_mulpd512", X86IRGenCategory::X86Specific,
"#include <immintrin.h>\n__m512d f(__m512d a, __m512d b) { return _mm512_mul_pd(a, b); }")
.with_flags(vec!["-mavx512f"])
.require("AVX-512 mulpd intrinsic", "llvm.x86.avx512"),
X86IRGenTest::new("ir_x86_avx512_gather", X86IRGenCategory::X86Specific,
"#include <immintrin.h>\n__m512 f(void *p, __m512i idx) { return _mm512_i32gather_ps(idx, p, 4); }")
.with_flags(vec!["-mavx512f"])
.require("AVX-512 gather intrinsic", "llvm.x86.avx512.gather"),
X86IRGenTest::new("ir_x86_fma_fmadd", X86IRGenCategory::X86Specific,
"#include <immintrin.h>\n__m256 f(__m256 a, __m256 b, __m256 c) { return _mm256_fmadd_ps(a, b, c); }")
.with_flags(vec!["-mfma"])
.require("FMA intrinsic", "llvm.fmuladd"),
X86IRGenTest::new("ir_x86_inline_asm_basic", X86IRGenCategory::InlineAsm,
"int f(void) { int x; __asm__(\"movl $42, %0\" : \"=r\"(x)); return x; }")
.require("inline assembly", "call void asm"),
X86IRGenTest::new("ir_x86_inline_asm_sideeffect", X86IRGenCategory::InlineAsm,
"void f(void) { __asm__ volatile(\"nop\"); }")
.require("volatile asm sideeffect", "sideeffect"),
X86IRGenTest::new("ir_x86_inline_asm_input", X86IRGenCategory::InlineAsm,
"int f(int a, int b) { int result; __asm__(\"addl %1, %0\" : \"=r\"(result) : \"r\"(a), \"0\"(b)); return result; }")
.require("inline asm with inputs", "call void asm"),
X86IRGenTest::new("ir_x86_inline_asm_clobber", X86IRGenCategory::InlineAsm,
"void f(void) { __asm__ volatile(\"cpuid\" : : : \"eax\", \"ebx\", \"ecx\", \"edx\"); }")
.require("inline asm with clobbers", "call void asm"),
X86IRGenTest::new("ir_x86_inline_asm_alignstack", X86IRGenCategory::InlineAsm,
"void f(void) { __asm__ volatile(\"nop\" : : : \"memory\"); }")
.require("asm with memory clobber", "~{memory}"),
X86IRGenTest::new("ir_x86_target_cpu", X86IRGenCategory::X86Specific,
"int f(void) { return 42; }")
.with_flags(vec!["-march=skylake"])
.require("target-cpu attribute", "target-cpu"),
X86IRGenTest::new("ir_x86_target_features", X86IRGenCategory::X86Specific,
"int f(void) { return 42; }")
.with_flags(vec!["-mavx2"])
.require("target-features attribute", "target-features"),
X86IRGenTest::new("ir_x86_target_attr_func", X86IRGenCategory::X86Specific,
"int f(void) __attribute__((target(\"sse4.2\"))); int f(void) { return 42; }")
.require("function target attribute", "target-features"),
X86IRGenTest::new("ir_x86_data_layout", X86IRGenCategory::X86Specific,
"int f(int x) { return x; }")
.with_target("x86_64-unknown-linux-gnu")
.require("data layout string", "target datalayout"),
X86IRGenTest::new("ir_x86_stack_alignment", X86IRGenCategory::X86Specific,
"struct S { char buf[1024]; }; void f(struct S s) {}")
.require("stack allocation", "alloca"),
X86IRGenTest::new("ir_x86_redzone", X86IRGenCategory::X86Specific,
"int f(void) { int x = 0; return x; }")
.with_target("x86_64-unknown-linux-gnu")
.require("function definition with norecurse or similar", "define"),
X86IRGenTest::new("ir_x86_bmi_blsr", X86IRGenCategory::X86Specific,
"unsigned f(unsigned x) { return x & (x - 1); }")
.with_flags(vec!["-mbmi"])
.require("BLSR pattern", "llvm.x86.bmi"),
X86IRGenTest::new("ir_x86_bmi_blsmsk", X86IRGenCategory::X86Specific,
"unsigned f(unsigned x) { return x ^ (x - 1); }")
.with_flags(vec!["-mbmi"])
.require("BLSMSK pattern", "llvm.x86.bmi"),
X86IRGenTest::new("ir_x86_lzcnt", X86IRGenCategory::X86Specific,
"unsigned f(unsigned x) { return __builtin_clz(x); }")
.with_flags(vec!["-mlzcnt"])
.require("LZCNT intrinsic", "llvm.ctlz"),
X86IRGenTest::new("ir_x86_tzcnt", X86IRGenCategory::X86Specific,
"unsigned f(unsigned x) { return __builtin_ctz(x); }")
.with_flags(vec!["-mbmi"])
.require("TZCNT intrinsic", "llvm.cttz"),
X86IRGenTest::new("ir_x86_popcnt", X86IRGenCategory::X86Specific,
"int f(unsigned x) { return __builtin_popcount(x); }")
.with_flags(vec!["-mpopcnt"])
.require("POPCNT intrinsic", "llvm.ctpop"),
X86IRGenTest::new("ir_x86_rdtsc", X86IRGenCategory::X86Specific,
"#include <x86intrin.h>\nunsigned long long f() { return __rdtsc(); }")
.require("RDTSC intrinsic", "llvm.x86.rdtsc"),
X86IRGenTest::new("ir_x86_rdtscp", X86IRGenCategory::X86Specific,
"#include <x86intrin.h>\nunsigned long long f(unsigned int *aux) { return __rdtscp(aux); }")
.require("RDTSCP intrinsic", "llvm.x86.rdtscp"),
X86IRGenTest::new("ir_x86_cpuid", X86IRGenCategory::X86Specific,
"#include <cpuid.h>\nvoid f(unsigned int *eax, unsigned int *ebx, unsigned int *ecx, unsigned int *edx) { __cpuid(0, *eax, *ebx, *ecx, *edx); }")
.require("CPUID inline asm", "cpuid"),
X86IRGenTest::new("ir_x86_cmpxchg16b", X86IRGenCategory::X86Specific,
"#include <stdatomic.h>\nbool f(_Atomic __int128 *p, __int128 *expected, __int128 desired) { return atomic_compare_exchange_strong(p, expected, desired); }")
.with_flags(vec!["-mcx16"])
.require("cmpxchg on i128", "cmpxchg i128"),
X86IRGenTest::new("ir_x86_fxsave", X86IRGenCategory::X86Specific,
"#include <x86intrin.h>\nvoid f(void *p) { _fxsave(p); }")
.require("FXSAVE intrinsic", "llvm.x86.fxsave"),
X86IRGenTest::new("ir_x86_mfence", X86IRGenCategory::X86Specific,
"#include <x86intrin.h>\nvoid f(void) { _mm_mfence(); }")
.require("MFENCE intrinsic", "llvm.x86.sse2.mfence"),
X86IRGenTest::new("ir_x86_sfence", X86IRGenCategory::X86Specific,
"#include <x86intrin.h>\nvoid f(void) { _mm_sfence(); }")
.require("SFENCE intrinsic", "fence"),
X86IRGenTest::new("ir_x86_lfence", X86IRGenCategory::X86Specific,
"#include <x86intrin.h>\nvoid f(void) { _mm_lfence(); }")
.require("LFENCE intrinsic", "fence"),
X86IRGenTest::new("ir_x86_pause", X86IRGenCategory::X86Specific,
"void f(void) { __asm__ volatile(\"pause\"); }")
.require("pause instruction in inline asm", "pause"),
X86IRGenTest::new("ir_x86_clflush", X86IRGenCategory::X86Specific,
"#include <x86intrin.h>\nvoid f(void *p) { _mm_clflush(p); }")
.require("CLFLUSH intrinsic", "llvm.x86.sse2.clflush"),
X86IRGenTest::new("ir_x86_avx512_mask_add", X86IRGenCategory::X86Specific,
"#include <immintrin.h>\n__m512 f(__m512 a, __m512 b, __mmask16 k) { return _mm512_mask_add_ps(a, k, b, a); }")
.with_flags(vec!["-mavx512f"])
.require("masked AVX-512 add", "llvm.x86.avx512"),
X86IRGenTest::new("ir_x86_avx512_compress", X86IRGenCategory::X86Specific,
"#include <immintrin.h>\n__m512 f(__m512 a, __mmask16 k, __m512 b) { return _mm512_mask_compress_ps(a, k, b); }")
.with_flags(vec!["-mavx512f"])
.require("AVX-512 compress intrinsic", "llvm.x86.avx512"),
X86IRGenTest::new("ir_x86_avx512_expand", X86IRGenCategory::X86Specific,
"#include <immintrin.h>\n__m512 f(__mmask16 k, __m512 a) { return _mm512_maskz_expand_ps(k, a); }")
.with_flags(vec!["-mavx512f"])
.require("AVX-512 expand intrinsic", "llvm.x86.avx512"),
X86IRGenTest::new("ir_x86_select_as_cmov", X86IRGenCategory::X86Specific,
"int f(int a, int b, int cond) { return cond ? a : b; }")
.require("select instruction for cmov", "select i32"),
X86IRGenTest::new("ir_x86_setcc", X86IRGenCategory::X86Specific,
"bool f(int a, int b) { return a < b; }")
.require("setcc via icmp + zext", "icmp")
.require("zext from i1 to i8", "zext"),
X86IRGenTest::new("ir_x86_prefetch", X86IRGenCategory::X86Specific,
"#include <xmmintrin.h>\nvoid f(void *p) { _mm_prefetch((const char*)p, _MM_HINT_T0); }")
.require("prefetch intrinsic", "llvm.prefetch"),
X86IRGenTest::new("ir_x86_stack_protector_strong", X86IRGenCategory::X86Specific,
"void f(char *buf) { buf[0] = 'x'; }")
.with_flags(vec!["-fstack-protector-strong"])
.require("stack protector cookie", "__stack_chk_guard")
.require("stack protector check", "__stack_chk_fail"),
]
}
pub fn build_stress_tests() -> Vec<X86IRGenTest> {
vec![
X86IRGenTest::new("ir_stress_deep_if_nesting", X86IRGenCategory::Stress,
"int f(int a, int b, int c, int d, int e, int f, int g, int h) { \
if (a) { if (b) { if (c) { if (d) { if (e) { if (f) { if (g) { if (h) { return 1; }}}}}} }} return 0; }")
.require("deep if nesting branches", "br")
.require_at_least("many conditional branches", "br i1", 3),
X86IRGenTest::new("ir_stress_large_switch", X86IRGenCategory::Stress,
"int f(int x) { switch(x) { \
case 0: case 1: case 2: case 3: case 4: \
case 5: case 6: case 7: case 8: case 9: \
case 10: case 11: case 12: case 13: case 14: case 15: \
return 1; default: return 0; } }")
.require("large switch", "switch"),
X86IRGenTest::new("ir_stress_many_params", X86IRGenCategory::Stress,
"int f(int a, int b, int c, int d, int e, int f_val, \
int g, int h, int i, int j, int k, int l) { \
return a + b + c + d + e + f_val + g + h + i + j + k + l; }")
.require_at_least("function with many params", "i32", 12),
X86IRGenTest::new("ir_stress_large_struct", X86IRGenCategory::Stress,
"struct Big { \
int a0; int a1; int a2; int a3; int a4; \
int a5; int a6; int a7; int a8; int a9; \
int a10; int a11; int a12; int a13; int a14; int a15; }; \
int f(struct Big b) { return b.a15; }")
.require("large struct type in IR", "type"),
X86IRGenTest::new("ir_stress_many_locals", X86IRGenCategory::Stress,
"int f(void) { \
int a = 1, b = 2, c = 3, d = 4, e = 5, \
f_val = 6, g = 7, h = 8, i = 9, j = 10; \
return a + b + c + d + e + f_val + g + h + i + j; }")
.require_at_least("many alloca instructions", "alloca", 8),
X86IRGenTest::new("ir_stress_many_calls", X86IRGenCategory::Stress,
"int g(int x) { return x + 1; } \
int h(int x) { return g(x) + g(x + 1) + g(x + 2); } \
int f(int x) { return h(x) + h(x + 1) + h(x + 2); }")
.require_at_least("multiple call instructions", "call", 5),
X86IRGenTest::new("ir_stress_complex_expr", X86IRGenCategory::Stress,
"int f(int a, int b, int c, int d) { \
return (a + b) * (c - d) / ((a & b) | (c ^ d)) + \
(a << 2) + (b >> 1) - ((~c) & d); }")
.require_at_least("many arithmetic ops", "add", 2)
.require_at_least("bitwise ops", "and", 1),
X86IRGenTest::new("ir_stress_ptr_chain", X86IRGenCategory::Stress,
"int f(int ********p) { return ********p; }")
.require("many pointer loads", "load"),
X86IRGenTest::new("ir_stress_wide_struct", X86IRGenCategory::Stress,
"struct Wide { long long a; double b; long long c; double d; long long e; double f; }; \
double f(struct Wide w) { return w.a + w.b + w.c + w.d + w.e + w.f; }")
.require("wide struct passing", "type"),
X86IRGenTest::new("ir_stress_mixed_types", X86IRGenCategory::Stress,
"int f(int a, float b, double c, char d, short e, long long f_val) { \
return (int)(a + (int)b + (int)c + d + e + (int)f_val); }")
.require_at_least("mixed type arithmetic", "sitofp", 1)
.require_at_least("truncation for narrow types", "trunc", 1),
X86IRGenTest::new("ir_stress_volatile_chain", X86IRGenCategory::Stress,
"int f(volatile int *p) { \
volatile int a = *p; volatile int b = a + 1; volatile int c = b * 2; \
volatile int d = c - 3; return d; }")
.require_at_least("volatile loads and stores", "volatile", 4),
X86IRGenTest::new("ir_stress_nested_ternary", X86IRGenCategory::Stress,
"int f(int a, int b, int c, int d) { \
return a ? (b ? (c ? d : 0) : 1) : (b ? 2 : (c ? 3 : 4)); }")
.require_at_least("nested select chains", "select", 3),
X86IRGenTest::new("ir_stress_bitfield_extract", X86IRGenCategory::Stress,
"struct S { unsigned a:1, b:2, c:3, d:4, e:5, f:6, g:7, h:4; }; \
unsigned f(struct S s) { return s.a + s.b + s.c + s.d + s.e + s.f + s.g + s.h; }")
.require("bitfield extraction operations", "and")
.require("bitfield shifts", "shl"),
X86IRGenTest::new("ir_stress_recursive_phi", X86IRGenCategory::Stress,
"int f(int n) { if (n <= 0) return 0; return n + f(n-1); }")
.require("recursive call", "call i32 @f"),
X86IRGenTest::new("ir_stress_complex_gep", X86IRGenCategory::Stress,
"struct A { int x; double y; }; struct B { struct A a[3]; int z; }; \
double f(struct B *b, int i) { return b->a[i].y; }")
.require("complex GEP chain", "getelementptr"),
X86IRGenTest::new("ir_stress_switch_with_fallthrough", X86IRGenCategory::Stress,
"int f(int x) { int r = 0; switch(x) { \
case 1: r += 1; /* fallthrough */ \
case 2: r += 2; break; \
case 3: r += 4; /* fallthrough */ \
default: r += 8; } return r; }")
.require("switch with fallthrough", "switch"),
X86IRGenTest::new("ir_stress_va_list", X86IRGenCategory::Stress,
"#include <stdarg.h>\nint f(int n, ...) { va_list ap; va_start(ap, n); \
int s = 0; for (int i = 0; i < n; i++) s += va_arg(ap, int); va_end(ap); return s; }")
.require("va_start usage", "va_start"),
X86IRGenTest::new("ir_stress_alloca_vla_nested", X86IRGenCategory::Stress,
"#include <alloca.h>\nvoid f(int n, int m) { \
int *a = alloca(n); int *b = alloca(m * 2); \
a[0] = b[0]; }")
.with_flags(vec!["-std=gnu99"])
.require_at_least("multiple alloca calls", "alloca", 3),
]
}
pub fn build_integration_tests() -> Vec<X86IRGenTest> {
vec![
X86IRGenTest::new(
"ir_integration_full_module",
X86IRGenCategory::Integration,
"int global = 42;\n\
static int hidden = 99;\n\
extern int external;\n\
struct Point { int x, y; };\n\
double compute(double a, double b, int op) {\n\
struct Point p = {1, 2};\n\
double result = a + b;\n\
if (op > 0) result = a * b;\n\
else if (op < 0) result = a / b;\n\
for (int i = 0; i < 10; i++) { result += i * 0.1; }\n\
return result + global + p.x;\n\
}",
)
.require("global variable", "@global")
.require("internal linkage for static", "internal")
.require("struct type", "type")
.require("getelementptr for struct field", "getelementptr")
.require("add instruction", "fadd double")
.require("conditional branch", "br i1")
.require("phi instruction (if-else merge)", "phi double")
.require("loop branch", "br label"),
X86IRGenTest::new(
"ir_integration_recursive",
X86IRGenCategory::Integration,
"int factorial(int n) { if (n <= 1) return 1; return n * factorial(n - 1); }",
)
.require("recursive call", "call i32 @factorial")
.require("conditional branch for base case", "br i1")
.require("icmp sle for n <= 1", "icmp"),
X86IRGenTest::new(
"ir_integration_fibonacci",
X86IRGenCategory::Integration,
"int fib(int n) { if (n < 2) return n; return fib(n-1) + fib(n-2); }",
)
.require("recursive function with two calls", "call i32 @fib"),
X86IRGenTest::new(
"ir_integration_pipeline",
X86IRGenCategory::Integration,
"static int counter = 0; \
struct Counter { int id; int (*inc)(struct Counter*); }; \
static int inc_fn(struct Counter *c) { counter++; return ++c->id; } \
int f(void) { \
struct Counter c = {0, inc_fn}; \
int a = c.inc(&c); \
int b = c.inc(&c); \
return a + b + counter; \
}",
)
.require("function pointer in struct", "ptr")
.require("indirect call", "call")
.require("static global counter", "@counter")
.require("internal function", "internal"),
X86IRGenTest::new(
"ir_integration_long_double_x86",
X86IRGenCategory::Integration,
"long double f(long double a, long double b, int op) { \
if (op == 1) return a + b; \
else if (op == 2) return a - b; \
else if (op == 3) return a * b; \
else return a / b; \
}",
)
.require("x86_fp80 type", "x86_fp80")
.require("long double arithmetic", "fadd x86_fp80")
.require("conditional for op selection", "switch"),
X86IRGenTest::new(
"ir_integration_printf",
X86IRGenCategory::Integration,
"#include <stdio.h>\nint f(void) { printf(\"hello %d\\n\", 42); return 0; }",
)
.require("call to printf", "call")
.require("string constant", "hello %d"),
]
}
pub fn build_x86_ir_gen_suite() -> X86IRGenTestSuite {
let mut suite = X86IRGenTestSuite::new(
"x86_ir_gen",
"Complete IR generation test suite for Clang on X86 — verifies correct \
LLVM IR output for all C/C++ language features across types, constants, \
instructions, memory operations, attributes, linkage, visibility, \
calling conventions, C patterns, C++ patterns, and X86-specific patterns.",
);
suite.add_many(build_type_tests());
suite.add_many(build_constant_tests());
suite.add_many(build_memory_instruction_tests());
suite.add_many(build_arithmetic_tests());
suite.add_many(build_bitwise_tests());
suite.add_many(build_comparison_tests());
suite.add_many(build_control_flow_tests());
suite.add_many(build_aggregate_tests());
suite.add_many(build_call_tests());
suite.add_many(build_memory_attr_tests());
suite.add_many(build_function_attr_tests());
suite.add_many(build_param_attr_tests());
suite.add_many(build_linkage_tests());
suite.add_many(build_visibility_tests());
suite.add_many(build_calling_convention_tests());
suite.add_many(build_debug_info_tests());
suite.add_many(build_optimization_tests());
suite.add_many(build_c_language_tests());
suite.add_many(build_cxx_tests());
suite.add_many(build_x86_specific_tests());
suite.add_many(build_stress_tests());
suite.add_many(build_integration_tests());
suite
}
pub fn total_test_count() -> usize {
let suite = build_x86_ir_gen_suite();
suite.len()
}
pub fn category_summary() -> BTreeMap<X86IRGenCategory, usize> {
let suite = build_x86_ir_gen_suite();
let mut map = BTreeMap::new();
for cat in &[
X86IRGenCategory::Types,
X86IRGenCategory::Constants,
X86IRGenCategory::Instructions,
X86IRGenCategory::Arithmetic,
X86IRGenCategory::MemoryAccess,
X86IRGenCategory::ControlFlow,
X86IRGenCategory::FunctionAttributes,
X86IRGenCategory::ParameterAttributes,
X86IRGenCategory::Linkage,
X86IRGenCategory::Visibility,
X86IRGenCategory::CallingConvention,
X86IRGenCategory::CLanguage,
X86IRGenCategory::CPlusPlus,
X86IRGenCategory::X86Specific,
X86IRGenCategory::Vectors,
X86IRGenCategory::InlineAsm,
X86IRGenCategory::ExceptionHandling,
X86IRGenCategory::Atomics,
X86IRGenCategory::Integration,
X86IRGenCategory::DebugInfo,
X86IRGenCategory::Optimization,
X86IRGenCategory::Stress,
] {
let count = suite.filter_by_category(*cat).len();
if count > 0 {
map.insert(*cat, count);
}
}
map
}
#[derive(Debug, Clone)]
pub struct X86IRGenTestRunner {
pub options: X86CompileOptions,
pub verbose: bool,
}
impl X86IRGenTestRunner {
pub fn new() -> Self {
Self {
options: X86CompileOptions {
target: "x86_64-unknown-linux-gnu".to_string(),
opt_level: X86OptLevel::O0,
debug_info: false,
flags: Vec::new(),
..Default::default()
},
verbose: false,
}
}
pub fn verbose(mut self, v: bool) -> Self {
self.verbose = v;
self
}
pub fn with_target(mut self, target: &str) -> Self {
self.options.target = target.to_string();
self
}
pub fn run_single(&self, test: &X86IRGenTest) -> X86IRGenTestResult {
use std::time::Instant;
let start = Instant::now();
let compile_result = compile_to_x86_ir(&test.source, &self.options);
let duration_ms = start.elapsed().as_millis() as u64;
match compile_result {
Ok(ir_text) => {
if !test.should_compile {
return X86IRGenTestResult {
name: test.name.clone(),
passed: false,
ir_text: Some(ir_text),
check_results: Vec::new(),
compiled: true,
compile_error: None,
duration_ms,
};
}
X86IRGenTestResult::from_test(test, &ir_text, true, None, duration_ms)
}
Err(err) => {
if !test.should_compile {
return X86IRGenTestResult {
name: test.name.clone(),
passed: true,
ir_text: None,
check_results: Vec::new(),
compiled: false,
compile_error: Some(format!("{:?}", err)),
duration_ms,
};
}
X86IRGenTestResult::compile_failure(test, &format!("{:?}", err), duration_ms)
}
}
}
pub fn run_suite(&self, suite: &X86IRGenTestSuite) -> Vec<X86IRGenTestResult> {
let mut results = Vec::with_capacity(suite.len());
for test in &suite.tests {
let result = self.run_single(test);
if self.verbose {
println!("{}", result.summary());
}
results.push(result);
}
results
}
pub fn run_all_and_summarize(&self) {
let suite = build_x86_ir_gen_suite();
let results = self.run_suite(&suite);
let passed = results.iter().filter(|r| r.passed).count();
let failed = results.len() - passed;
println!("═══════════════════════════════════════════════════════════");
println!(" X86 IR Generation Test Results");
println!("═══════════════════════════════════════════════════════════");
println!(" Total: {}", results.len());
println!(" Passed: {}", passed);
println!(" Failed: {}", failed);
println!("═══════════════════════════════════════════════════════════");
if failed > 0 {
println!("\n Failed tests:");
for r in &results {
if !r.passed {
println!(" ✗ {}", r.name);
for check in r.failed_checks() {
println!(" - {}", check);
}
}
}
}
let cat_summary = category_summary();
println!("\n Category breakdown:");
for (cat, count) in &cat_summary {
let cat_results: Vec<_> = results
.iter()
.filter(|r| {
suite
.tests
.iter()
.any(|t| t.name == r.name && t.category == *cat)
})
.collect();
let cat_passed = cat_results.iter().filter(|r| r.passed).count();
println!(
" {:20} {:>4} tests, {:>4} passed",
cat.as_str(),
count,
cat_passed
);
}
}
}
impl Default for X86IRGenTestRunner {
fn default() -> Self {
Self::new()
}
}
impl Default for X86CompileOptions {
fn default() -> Self {
Self {
target: "x86_64-unknown-linux-gnu".to_string(),
opt_level: X86OptLevel::O0,
debug_info: false,
flags: Vec::new(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_pattern_required_found() {
let pat = X86IRPattern::required("test", "hello");
assert!(pat.check("hello world"));
}
#[test]
fn test_pattern_required_not_found() {
let pat = X86IRPattern::required("test", "zzz");
assert!(!pat.check("hello world"));
}
#[test]
fn test_pattern_exact_count_match() {
let pat = X86IRPattern::exact("test", "a", 3);
assert!(pat.check("a a a"));
assert!(!pat.check("a a"));
assert!(!pat.check("a a a a"));
}
#[test]
fn test_pattern_at_least() {
let pat = X86IRPattern::at_least("test", "x", 3);
assert!(pat.check("x x x"));
assert!(pat.check("x x x x"));
assert!(!pat.check("x x"));
}
#[test]
fn test_pattern_forbidden() {
let pat = X86IRPattern::forbidden("test", "bad");
assert!(pat.check("good stuff"));
}
#[test]
fn test_pattern_forbidden_found() {
let pat = X86IRPattern::forbidden("test", "bad");
assert!(!pat.check("this is bad stuff"));
}
#[test]
fn test_pattern_case_insensitive() {
let pat = X86IRPattern::required("test", "ADD");
assert!(pat.check("the add instruction"));
}
#[test]
fn test_pattern_case_sensitive() {
let mut pat = X86IRPattern::required("test", "ADD");
pat.case_sensitive = true;
assert!(!pat.check("the add instruction"));
assert!(pat.check("the ADD instruction"));
}
#[test]
fn test_test_new() {
let t = X86IRGenTest::new(
"mytest",
X86IRGenCategory::Types,
"int f(void) { return 0; }",
);
assert_eq!(t.name, "mytest");
assert_eq!(t.category, X86IRGenCategory::Types);
assert!(!t.source.is_empty());
assert!(t.should_compile);
assert!(!t.is_xfail);
}
#[test]
fn test_test_builder() {
let t = X86IRGenTest::new(
"test",
X86IRGenCategory::Arithmetic,
"int f(int a, int b) { return a + b; }",
)
.with_target("i686-unknown-linux-gnu")
.with_flags(vec!["-O2", "-Wall"])
.with_opt(X86OptLevel::O2)
.require("add", "add")
.require_exact("exact", "add", 1)
.forbid("nofmul", "fmul")
.with_expected_return(42);
assert_eq!(t.target_triple, "i686-unknown-linux-gnu");
assert_eq!(t.flags.len(), 2);
assert_eq!(t.required_ir.len(), 2);
assert_eq!(t.forbidden_ir.len(), 1);
assert_eq!(t.expected_return, Some(42));
}
#[test]
fn test_test_xfail() {
let t = X86IRGenTest::new("fail", X86IRGenCategory::Types, "bad syntax")
.xfail("Parser cannot handle this yet");
assert!(t.is_xfail);
assert_eq!(
t.xfail_reason,
Some("Parser cannot handle this yet".to_string())
);
}
#[test]
fn test_result_all_pass() {
let test = X86IRGenTest::new("test", X86IRGenCategory::Types, "").require("found", "test");
let result = X86IRGenTestResult::from_test(&test, "this is a test string", true, None, 10);
assert!(result.passed);
assert_eq!(result.check_results.len(), 1);
assert!(result.check_results[0].passed);
}
#[test]
fn test_result_some_fail() {
let test = X86IRGenTest::new("test", X86IRGenCategory::Types, "")
.require("found", "present")
.require("not_found", "zzz_nope");
let result =
X86IRGenTestResult::from_test(&test, "this string has present in it", true, None, 5);
assert!(!result.passed);
assert_eq!(result.check_results.len(), 2);
assert!(result.check_results[0].passed); assert!(!result.check_results[1].passed); }
#[test]
fn test_result_compile_failure() {
let test =
X86IRGenTest::new("cfail", X86IRGenCategory::Types, "bad").expect_compile_failure();
let result = X86IRGenTestResult::compile_failure(&test, "parse error", 15);
assert!(result.passed); assert!(!result.compiled);
assert!(result.ir_text.is_none());
}
#[test]
fn test_result_compile_failure_when_expected_success() {
let test = X86IRGenTest::new("cfail", X86IRGenCategory::Types, "bad");
let result = X86IRGenTestResult::compile_failure(&test, "parse error", 15);
assert!(!result.passed); }
#[test]
fn test_result_summary_format() {
let test = X86IRGenTest::new("mytest", X86IRGenCategory::Types, "");
let result = X86IRGenTestResult::from_test(&test, "hello", true, None, 1);
let summary = result.summary();
assert!(summary.contains("mytest"));
assert!(summary.contains("PASS") || summary.contains("FAIL"));
}
#[test]
fn test_result_failed_checks() {
let test = X86IRGenTest::new("f", X86IRGenCategory::Types, "")
.require("a", "present")
.require("b", "absent_in_text");
let result = X86IRGenTestResult::from_test(&test, "present in text", true, None, 1);
let failed = result.failed_checks();
assert_eq!(failed.len(), 1);
assert!(failed[0].contains("absent_in_text") || failed[0].contains("b"));
}
#[test]
fn test_result_failed_count() {
let test = X86IRGenTest::new("f", X86IRGenCategory::Types, "")
.require("a", "x")
.require("b", "y")
.require("c", "z")
.forbid("d", "x");
let result = X86IRGenTestResult::from_test(&test, "just x here", true, None, 1);
assert_eq!(result.failed_count(), 3); }
#[test]
fn test_suite_creation() {
let suite = X86IRGenTestSuite::new("test", "A test suite");
assert_eq!(suite.name, "test");
assert_eq!(suite.description, "A test suite");
assert!(suite.is_empty());
assert_eq!(suite.len(), 0);
}
#[test]
fn test_suite_add() {
let mut suite = X86IRGenTestSuite::new("s", "d");
suite.add(X86IRGenTest::new("t1", X86IRGenCategory::Types, ""));
suite.add(X86IRGenTest::new("t2", X86IRGenCategory::Arithmetic, ""));
assert_eq!(suite.len(), 2);
}
#[test]
fn test_suite_add_many() {
let mut suite = X86IRGenTestSuite::new("s", "d");
let tests = vec![
X86IRGenTest::new("t1", X86IRGenCategory::Types, ""),
X86IRGenTest::new("t2", X86IRGenCategory::Arithmetic, ""),
X86IRGenTest::new("t3", X86IRGenCategory::Types, ""),
];
suite.add_many(tests);
assert_eq!(suite.len(), 3);
}
#[test]
fn test_suite_filter_by_category() {
let mut suite = X86IRGenTestSuite::new("s", "d");
suite.add(X86IRGenTest::new("t1", X86IRGenCategory::Types, ""));
suite.add(X86IRGenTest::new("t2", X86IRGenCategory::Arithmetic, ""));
suite.add(X86IRGenTest::new("t3", X86IRGenCategory::Types, ""));
let types = suite.filter_by_category(X86IRGenCategory::Types);
assert_eq!(types.len(), 2);
}
#[test]
fn test_suite_filter_by_name() {
let mut suite = X86IRGenTestSuite::new("s", "d");
suite.add(X86IRGenTest::new("foo_bar", X86IRGenCategory::Types, ""));
suite.add(X86IRGenTest::new("foo_baz", X86IRGenCategory::Types, ""));
suite.add(X86IRGenTest::new("qux", X86IRGenCategory::Types, ""));
let filtered = suite.filter_by_name("foo");
assert_eq!(filtered.len(), 2);
}
#[test]
fn test_type_tests_not_empty() {
let tests = build_type_tests();
assert!(!tests.is_empty());
}
#[test]
fn test_constant_tests_not_empty() {
let tests = build_constant_tests();
assert!(!tests.is_empty());
}
#[test]
fn test_memory_instruction_tests_not_empty() {
let tests = build_memory_instruction_tests();
assert!(!tests.is_empty());
}
#[test]
fn test_arithmetic_tests_not_empty() {
let tests = build_arithmetic_tests();
assert!(!tests.is_empty());
}
#[test]
fn test_bitwise_tests_not_empty() {
let tests = build_bitwise_tests();
assert!(!tests.is_empty());
}
#[test]
fn test_comparison_tests_not_empty() {
let tests = build_comparison_tests();
assert!(!tests.is_empty());
}
#[test]
fn test_control_flow_tests_not_empty() {
let tests = build_control_flow_tests();
assert!(!tests.is_empty());
}
#[test]
fn test_aggregate_tests_not_empty() {
let tests = build_aggregate_tests();
assert!(!tests.is_empty());
}
#[test]
fn test_call_tests_not_empty() {
let tests = build_call_tests();
assert!(!tests.is_empty());
}
#[test]
fn test_memory_attr_tests_not_empty() {
let tests = build_memory_attr_tests();
assert!(!tests.is_empty());
}
#[test]
fn test_function_attr_tests_not_empty() {
let tests = build_function_attr_tests();
assert!(!tests.is_empty());
}
#[test]
fn test_param_attr_tests_not_empty() {
let tests = build_param_attr_tests();
assert!(!tests.is_empty());
}
#[test]
fn test_linkage_tests_not_empty() {
let tests = build_linkage_tests();
assert!(!tests.is_empty());
}
#[test]
fn test_visibility_tests_not_empty() {
let tests = build_visibility_tests();
assert!(!tests.is_empty());
}
#[test]
fn test_calling_convention_tests_not_empty() {
let tests = build_calling_convention_tests();
assert!(!tests.is_empty());
}
#[test]
fn test_c_language_tests_not_empty() {
let tests = build_c_language_tests();
assert!(!tests.is_empty());
}
#[test]
fn test_cxx_tests_not_empty() {
let tests = build_cxx_tests();
assert!(!tests.is_empty());
}
#[test]
fn test_x86_specific_tests_not_empty() {
let tests = build_x86_specific_tests();
assert!(!tests.is_empty());
}
#[test]
fn test_stress_tests_not_empty() {
let tests = build_stress_tests();
assert!(!tests.is_empty());
}
#[test]
fn test_integration_tests_not_empty() {
let tests = build_integration_tests();
assert!(!tests.is_empty());
}
#[test]
fn test_full_suite_not_empty() {
let suite = build_x86_ir_gen_suite();
assert!(!suite.is_empty());
assert!(suite.len() > 50);
}
#[test]
fn test_total_test_count() {
let count = total_test_count();
assert!(count > 50, "Expected >50 tests, got {}", count);
}
#[test]
fn test_category_summary_has_keys() {
let summary = category_summary();
assert!(!summary.is_empty());
assert!(summary.contains_key(&X86IRGenCategory::Types));
assert!(summary.contains_key(&X86IRGenCategory::Arithmetic));
assert!(summary.contains_key(&X86IRGenCategory::ControlFlow));
}
#[test]
fn test_category_summary_sums_to_total() {
let summary = category_summary();
let total_cat: usize = summary.values().sum();
let total_all = total_test_count();
assert_eq!(total_cat, total_all);
}
#[test]
fn test_category_as_str() {
assert_eq!(X86IRGenCategory::Types.as_str(), "types");
assert_eq!(X86IRGenCategory::Constants.as_str(), "constants");
assert_eq!(X86IRGenCategory::Instructions.as_str(), "instructions");
assert_eq!(X86IRGenCategory::Arithmetic.as_str(), "arithmetic");
assert_eq!(X86IRGenCategory::MemoryAccess.as_str(), "memory");
assert_eq!(X86IRGenCategory::ControlFlow.as_str(), "control_flow");
assert_eq!(X86IRGenCategory::FunctionAttributes.as_str(), "fn_attrs");
assert_eq!(
X86IRGenCategory::ParameterAttributes.as_str(),
"param_attrs"
);
assert_eq!(X86IRGenCategory::Linkage.as_str(), "linkage");
assert_eq!(X86IRGenCategory::Visibility.as_str(), "visibility");
assert_eq!(X86IRGenCategory::CallingConvention.as_str(), "calling_conv");
assert_eq!(X86IRGenCategory::CLanguage.as_str(), "c_lang");
assert_eq!(X86IRGenCategory::CPlusPlus.as_str(), "cxx");
assert_eq!(X86IRGenCategory::X86Specific.as_str(), "x86");
assert_eq!(X86IRGenCategory::Vectors.as_str(), "vectors");
assert_eq!(X86IRGenCategory::InlineAsm.as_str(), "inline_asm");
assert_eq!(X86IRGenCategory::ExceptionHandling.as_str(), "eh");
assert_eq!(X86IRGenCategory::Atomics.as_str(), "atomics");
assert_eq!(X86IRGenCategory::Integration.as_str(), "integration");
assert_eq!(X86IRGenCategory::DebugInfo.as_str(), "debug_info");
assert_eq!(X86IRGenCategory::Optimization.as_str(), "optimization");
assert_eq!(X86IRGenCategory::Stress.as_str(), "stress");
}
#[test]
fn test_type_tests_cover_void() {
let tests = build_type_tests();
let void_tests: Vec<_> = tests.iter().filter(|t| t.name.contains("void")).collect();
assert!(!void_tests.is_empty());
}
#[test]
fn test_type_tests_cover_i32() {
let tests = build_type_tests();
let i32_tests: Vec<_> = tests.iter().filter(|t| t.name.contains("i32")).collect();
assert!(!i32_tests.is_empty());
}
#[test]
fn test_type_tests_cover_pointer() {
let tests = build_type_tests();
let ptr_tests: Vec<_> = tests.iter().filter(|t| t.name.contains("ptr")).collect();
assert!(!ptr_tests.is_empty());
}
#[test]
fn test_type_tests_cover_vector() {
let tests = build_type_tests();
let vec_tests: Vec<_> = tests.iter().filter(|t| t.name.contains("vector")).collect();
assert!(!vec_tests.is_empty());
}
#[test]
fn test_cmp_tests_cover_all_icmp_predicates() {
let tests = build_comparison_tests();
let predicates = [
"eq", "ne", "sgt", "sge", "slt", "sle", "ugt", "uge", "ult", "ule",
];
for pred in &predicates {
let found = tests
.iter()
.any(|t| t.name.contains(&format!("icmp_{}", pred)));
assert!(found, "Missing icmp predicate: {}", pred);
}
}
#[test]
fn test_cmp_tests_cover_fcmp_predicates() {
let tests = build_comparison_tests();
let predicates = ["oeq", "one", "ogt", "oge", "olt", "ole"];
for pred in &predicates {
let found = tests
.iter()
.any(|t| t.name.contains(&format!("fcmp_{}", pred)));
assert!(found, "Missing fcmp predicate: {}", pred);
}
}
#[test]
fn test_memory_tests_cover_volatile() {
let tests = build_memory_attr_tests();
let vol_tests: Vec<_> = tests
.iter()
.filter(|t| t.name.contains("volatile"))
.collect();
assert!(!vol_tests.is_empty());
}
#[test]
fn test_memory_tests_cover_atomic() {
let tests = build_memory_attr_tests();
let atom_tests: Vec<_> = tests.iter().filter(|t| t.name.contains("atomic")).collect();
assert!(!atom_tests.is_empty());
}
#[test]
fn test_attribute_tests_cover_nounwind() {
let tests = build_function_attr_tests();
let nw_tests: Vec<_> = tests
.iter()
.filter(|t| t.name.contains("nounwind"))
.collect();
assert!(!nw_tests.is_empty());
}
#[test]
fn test_attribute_tests_cover_sanitizer() {
let tests = build_function_attr_tests();
let san_tests: Vec<_> = tests
.iter()
.filter(|t| t.name.contains("sanitize"))
.collect();
assert!(!san_tests.is_empty());
}
#[test]
fn test_linkage_tests_cover_weak() {
let tests = build_linkage_tests();
let weak_tests: Vec<_> = tests.iter().filter(|t| t.name.contains("weak")).collect();
assert!(!weak_tests.is_empty());
}
#[test]
fn test_linkage_tests_cover_internal() {
let tests = build_linkage_tests();
let int_tests: Vec<_> = tests
.iter()
.filter(|t| t.name.contains("internal"))
.collect();
assert!(!int_tests.is_empty());
}
#[test]
fn test_calling_conv_tests_cover_ccc() {
let tests = build_calling_convention_tests();
let ccc_tests: Vec<_> = tests.iter().filter(|t| t.name.contains("ccc")).collect();
assert!(!ccc_tests.is_empty());
}
#[test]
fn test_c_lang_tests_cover_compound_literal() {
let tests = build_c_language_tests();
let cl_tests: Vec<_> = tests
.iter()
.filter(|t| t.name.contains("compound"))
.collect();
assert!(!cl_tests.is_empty());
}
#[test]
fn test_c_lang_tests_cover_vla() {
let tests = build_c_language_tests();
let vla_tests: Vec<_> = tests.iter().filter(|t| t.name.contains("vla")).collect();
assert!(!vla_tests.is_empty());
}
#[test]
fn test_cxx_tests_cover_vtable() {
let tests = build_cxx_tests();
let vt_tests: Vec<_> = tests.iter().filter(|t| t.name.contains("vtable")).collect();
assert!(!vt_tests.is_empty());
}
#[test]
fn test_cxx_tests_cover_rtti() {
let tests = build_cxx_tests();
let rtti_tests: Vec<_> = tests.iter().filter(|t| t.name.contains("rtti")).collect();
assert!(!rtti_tests.is_empty());
}
#[test]
fn test_cxx_tests_cover_lambda() {
let tests = build_cxx_tests();
let lam_tests: Vec<_> = tests.iter().filter(|t| t.name.contains("lambda")).collect();
assert!(!lam_tests.is_empty());
}
#[test]
fn test_cxx_tests_cover_template() {
let tests = build_cxx_tests();
let tpl_tests: Vec<_> = tests
.iter()
.filter(|t| t.name.contains("template"))
.collect();
assert!(!tpl_tests.is_empty());
}
#[test]
fn test_cxx_tests_cover_eh_landingpad() {
let tests = build_cxx_tests();
let lp_tests: Vec<_> = tests
.iter()
.filter(|t| t.name.contains("landingpad"))
.collect();
assert!(!lp_tests.is_empty());
}
#[test]
fn test_x86_tests_cover_sse() {
let tests = build_x86_specific_tests();
let sse_tests: Vec<_> = tests.iter().filter(|t| t.name.contains("sse")).collect();
assert!(!sse_tests.is_empty());
}
#[test]
fn test_x86_tests_cover_avx() {
let tests = build_x86_specific_tests();
let avx_tests: Vec<_> = tests.iter().filter(|t| t.name.contains("avx")).collect();
assert!(!avx_tests.is_empty());
}
#[test]
fn test_x86_tests_cover_inline_asm() {
let tests = build_x86_specific_tests();
let asm_tests: Vec<_> = tests
.iter()
.filter(|t| t.name.contains("inline_asm"))
.collect();
assert!(!asm_tests.is_empty());
}
#[test]
fn test_stress_tests_cover_deep_nesting() {
let tests = build_stress_tests();
let deep_tests: Vec<_> = tests.iter().filter(|t| t.name.contains("deep")).collect();
assert!(!deep_tests.is_empty());
}
#[test]
fn test_all_required_patterns_are_well_formed() {
let suite = build_x86_ir_gen_suite();
for test in &suite.tests {
for pat in &test.required_ir {
assert!(
!pat.pattern.is_empty(),
"Empty pattern in test '{}': {}",
test.name,
pat.description
);
assert!(
!pat.pattern.trim().is_empty(),
"Whitespace-only pattern in test '{}': {}",
test.name,
pat.description
);
}
for pat in &test.forbidden_ir {
assert!(
!pat.pattern.is_empty(),
"Empty forbidden pattern in test '{}': {}",
test.name,
pat.description
);
}
}
}
#[test]
fn test_all_tests_have_source() {
let suite = build_x86_ir_gen_suite();
for test in &suite.tests {
assert!(
!test.source.is_empty(),
"Empty source in test '{}'",
test.name
);
}
}
#[test]
fn test_all_tests_have_unique_names() {
let suite = build_x86_ir_gen_suite();
let mut names = HashSet::new();
for test in &suite.tests {
if !names.insert(&test.name) {
panic!("Duplicate test name: '{}'", test.name);
}
}
}
#[test]
fn test_runner_new() {
let runner = X86IRGenTestRunner::new();
assert!(!runner.verbose);
}
#[test]
fn test_runner_verbose() {
let runner = X86IRGenTestRunner::new().verbose(true);
assert!(runner.verbose);
}
#[test]
fn test_runner_with_target() {
let runner = X86IRGenTestRunner::new().with_target("i686-unknown-linux-gnu");
assert_eq!(runner.options.target, "i686-unknown-linux-gnu");
}
#[test]
fn test_runner_default() {
let runner: X86IRGenTestRunner = Default::default();
assert!(!runner.verbose);
}
#[test]
fn test_runner_run_single_trivial() {
let runner = X86IRGenTestRunner::new();
let test = X86IRGenTest::new("trivial", X86IRGenCategory::Types, "");
let result = runner.run_single(&test);
assert_eq!(result.name, "trivial");
}
#[test]
fn test_suite_builder_consistent() {
let count1 = build_x86_ir_gen_suite().len();
let count2 = build_x86_ir_gen_suite().len();
assert_eq!(count1, count2);
}
#[test]
fn test_no_panic_on_min_checks() {
let _ = build_type_tests();
let _ = build_constant_tests();
let _ = build_memory_instruction_tests();
let _ = build_arithmetic_tests();
let _ = build_bitwise_tests();
let _ = build_comparison_tests();
let _ = build_control_flow_tests();
let _ = build_aggregate_tests();
let _ = build_call_tests();
let _ = build_memory_attr_tests();
let _ = build_function_attr_tests();
let _ = build_param_attr_tests();
let _ = build_linkage_tests();
let _ = build_visibility_tests();
let _ = build_calling_convention_tests();
let _ = build_c_language_tests();
let _ = build_cxx_tests();
let _ = build_x86_specific_tests();
let _ = build_stress_tests();
let _ = build_integration_tests();
}
#[test]
fn test_ir_gen_category_equality() {
assert_eq!(X86IRGenCategory::Types, X86IRGenCategory::Types);
assert_ne!(X86IRGenCategory::Types, X86IRGenCategory::Arithmetic);
}
#[test]
fn test_ir_gen_category_hash() {
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
let mut h1 = DefaultHasher::new();
let mut h2 = DefaultHasher::new();
X86IRGenCategory::Types.hash(&mut h1);
X86IRGenCategory::Types.hash(&mut h2);
assert_eq!(h1.finish(), h2.finish());
}
#[test]
fn test_pattern_count_basic() {
let pat = X86IRPattern::required("test", "add");
let text = "add sub add mul add";
assert_eq!(pat.count_in(text), 3);
}
#[test]
fn test_pattern_count_case_insensitive() {
let pat = X86IRPattern::required("test", "ADD");
let text = "add sub Add aDd";
assert_eq!(pat.count_in(text), 3);
}
#[test]
fn test_pattern_count_zero_for_nonexistent() {
let pat = X86IRPattern::required("test", "nonexistent");
assert_eq!(pat.count_in("some text"), 0);
}
#[test]
fn test_result_ir_text_preserved() {
let test = X86IRGenTest::new("t", X86IRGenCategory::Types, "");
let result = X86IRGenTestResult::from_test(&test, "define i32 @f", true, None, 1);
assert_eq!(result.ir_text, Some("define i32 @f".to_string()));
}
#[test]
fn test_pattern_count_overlapping() {
let pat = X86IRPattern::required("test", "aa");
assert_eq!(pat.count_in("aaa"), 1); }
#[test]
fn test_pattern_exact_zero() {
let pat = X86IRPattern::exact("test", "x", 0);
assert!(pat.check("no x here"));
assert!(!pat.check("x here"));
}
#[test]
fn test_pattern_check_edge_cases() {
let pat = X86IRPattern::required("test", "");
assert!(pat.check("anything"));
}
#[test]
fn test_result_forbidden_check_passes_when_absent() {
let test = X86IRGenTest::new("f", X86IRGenCategory::Types, "").forbid("no_mul", "mul");
let result = X86IRGenTestResult::from_test(&test, "add sub div", true, None, 1);
assert!(result.passed);
}
#[test]
fn test_result_forbidden_check_fails_when_present() {
let test = X86IRGenTest::new("f", X86IRGenCategory::Types, "").forbid("no_mul", "mul");
let result = X86IRGenTestResult::from_test(&test, "add mul div", true, None, 1);
assert!(!result.passed);
}
#[test]
fn test_result_exact_count_passes() {
let test = X86IRGenTest::new("e", X86IRGenCategory::Types, "").require_exact(
"exactly two adds",
"add",
2,
);
let result = X86IRGenTestResult::from_test(&test, "add sub add", true, None, 5);
assert!(result.passed);
}
#[test]
fn test_result_exact_count_fails_under() {
let test = X86IRGenTest::new("e", X86IRGenCategory::Types, "").require_exact(
"exactly two adds",
"add",
2,
);
let result = X86IRGenTestResult::from_test(&test, "add sub", true, None, 5);
assert!(!result.passed);
}
#[test]
fn test_result_exact_count_fails_over() {
let test = X86IRGenTest::new("e", X86IRGenCategory::Types, "").require_exact(
"exactly two adds",
"add",
2,
);
let result = X86IRGenTestResult::from_test(&test, "add add add", true, None, 5);
assert!(!result.passed);
}
#[test]
fn test_result_at_least_passes_at_boundary() {
let test =
X86IRGenTest::new("a", X86IRGenCategory::Types, "").require_at_least("min 2", "add", 2);
let result = X86IRGenTestResult::from_test(&test, "add add", true, None, 5);
assert!(result.passed);
}
#[test]
fn test_result_at_least_fails_below_boundary() {
let test =
X86IRGenTest::new("a", X86IRGenCategory::Types, "").require_at_least("min 2", "add", 2);
let result = X86IRGenTestResult::from_test(&test, "add", true, None, 5);
assert!(!result.passed);
}
#[test]
fn test_result_names_match() {
let test = X86IRGenTest::new("hello_test", X86IRGenCategory::Types, "");
let result = X86IRGenTestResult::from_test(&test, "x", true, None, 0);
assert_eq!(result.name, "hello_test");
}
#[test]
fn test_category_hash_consistent() {
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
let categories = vec![
X86IRGenCategory::Types,
X86IRGenCategory::Constants,
X86IRGenCategory::Instructions,
X86IRGenCategory::Arithmetic,
X86IRGenCategory::MemoryAccess,
X86IRGenCategory::ControlFlow,
];
for cat in &categories {
let mut h1 = DefaultHasher::new();
let mut h2 = DefaultHasher::new();
cat.hash(&mut h1);
cat.hash(&mut h2);
assert_eq!(h1.finish(), h2.finish());
}
}
#[test]
fn test_suite_filter_empty() {
let suite = X86IRGenTestSuite::new("empty", "");
let filtered = suite.filter_by_category(X86IRGenCategory::Types);
assert!(filtered.is_empty());
}
#[test]
fn test_suite_filter_name_no_match() {
let mut suite = X86IRGenTestSuite::new("s", "");
suite.add(X86IRGenTest::new("abc", X86IRGenCategory::Types, ""));
let filtered = suite.filter_by_name("xyz");
assert!(filtered.is_empty());
}
#[test]
fn test_suite_len_after_add_many() {
let mut suite = X86IRGenTestSuite::new("s", "");
suite.add_many(vec![
X86IRGenTest::new("a", X86IRGenCategory::Types, ""),
X86IRGenTest::new("b", X86IRGenCategory::Types, ""),
X86IRGenTest::new("c", X86IRGenCategory::Types, ""),
X86IRGenTest::new("d", X86IRGenCategory::Types, ""),
X86IRGenTest::new("e", X86IRGenCategory::Types, ""),
]);
assert_eq!(suite.len(), 5);
}
#[test]
fn test_x86_tests_cover_avx512() {
let tests = build_x86_specific_tests();
let avx512: Vec<_> = tests.iter().filter(|t| t.name.contains("avx512")).collect();
assert!(!avx512.is_empty());
}
#[test]
fn test_x86_tests_cover_fma() {
let tests = build_x86_specific_tests();
let fma: Vec<_> = tests.iter().filter(|t| t.name.contains("fma")).collect();
assert!(!fma.is_empty());
}
#[test]
fn test_cxx_tests_cover_new_delete() {
let tests = build_cxx_tests();
let nd: Vec<_> = tests
.iter()
.filter(|t| t.name.contains("new") || t.name.contains("delete"))
.collect();
assert!(!nd.is_empty());
}
#[test]
fn test_cxx_tests_cover_ctor_dtor() {
let tests = build_cxx_tests();
let cd: Vec<_> = tests
.iter()
.filter(|t| t.name.contains("ctor") || t.name.contains("dtor"))
.collect();
assert!(!cd.is_empty());
}
#[test]
fn test_debug_info_tests_not_empty() {
let tests = build_debug_info_tests();
assert!(!tests.is_empty());
}
#[test]
fn test_optimization_tests_not_empty() {
let tests = build_optimization_tests();
assert!(!tests.is_empty());
}
#[test]
fn test_all_categories_have_tests() {
let suite = build_x86_ir_gen_suite();
let categories = [
X86IRGenCategory::Types,
X86IRGenCategory::Constants,
X86IRGenCategory::Arithmetic,
X86IRGenCategory::MemoryAccess,
X86IRGenCategory::ControlFlow,
X86IRGenCategory::FunctionAttributes,
X86IRGenCategory::ParameterAttributes,
X86IRGenCategory::Linkage,
X86IRGenCategory::Visibility,
X86IRGenCategory::CallingConvention,
X86IRGenCategory::CLanguage,
X86IRGenCategory::CPlusPlus,
X86IRGenCategory::X86Specific,
];
for cat in &categories {
let count = suite.filter_by_category(*cat).len();
assert!(count > 0, "Category {:?} has no tests", cat);
}
}
#[test]
fn test_all_patterns_nonempty_in_type_tests() {
for test in &build_type_tests() {
for pat in &test.required_ir {
assert!(
!pat.pattern.is_empty(),
"Empty pattern in test '{}': '{}'",
test.name,
pat.description
);
}
for pat in &test.forbidden_ir {
assert!(
!pat.pattern.is_empty(),
"Empty forbidden pattern in test '{}' : '{}'",
test.name,
pat.description
);
}
}
}
#[test]
fn test_all_patterns_nonempty_in_constant_tests() {
for test in &build_constant_tests() {
for pat in &test.required_ir {
assert!(!pat.pattern.is_empty());
}
}
}
#[test]
fn test_all_patterns_nonempty_in_arithmetic_tests() {
for test in &build_arithmetic_tests() {
for pat in &test.required_ir {
assert!(!pat.pattern.is_empty());
}
}
}
#[test]
fn test_all_patterns_nonempty_in_comparison_tests() {
for test in &build_comparison_tests() {
for pat in &test.required_ir {
assert!(!pat.pattern.is_empty());
}
}
}
#[test]
fn test_all_patterns_nonempty_in_control_flow_tests() {
for test in &build_control_flow_tests() {
for pat in &test.required_ir {
assert!(!pat.pattern.is_empty());
}
}
}
#[test]
fn test_all_patterns_nonempty_in_linkage_tests() {
for test in &build_linkage_tests() {
for pat in &test.required_ir {
assert!(!pat.pattern.is_empty());
}
}
}
#[test]
fn test_all_patterns_nonempty_in_calling_convention_tests() {
for test in &build_calling_convention_tests() {
for pat in &test.required_ir {
assert!(!pat.pattern.is_empty());
}
}
}
#[test]
fn test_all_patterns_nonempty_in_c_tests() {
for test in &build_c_language_tests() {
for pat in &test.required_ir {
assert!(!pat.pattern.is_empty());
}
}
}
#[test]
fn test_all_patterns_nonempty_in_cxx_tests() {
for test in &build_cxx_tests() {
for pat in &test.required_ir {
assert!(!pat.pattern.is_empty());
}
}
}
#[test]
fn test_all_patterns_nonempty_in_x86_tests() {
for test in &build_x86_specific_tests() {
for pat in &test.required_ir {
assert!(!pat.pattern.is_empty());
}
}
}
#[test]
fn test_type_map_not_empty() {
let map = x86_type_map();
assert!(!map.is_empty());
}
#[test]
fn test_type_map_has_i32() {
let map = x86_type_map();
let has_i32 = map.iter().any(|t| t.ir_type == "i32");
assert!(has_i32);
}
#[test]
fn test_type_map_has_double() {
let map = x86_type_map();
let has_double = map.iter().any(|t| t.ir_type == "double");
assert!(has_double);
}
#[test]
fn test_type_map_has_pointer() {
let map = x86_type_map();
let has_ptr = map.iter().any(|t| t.ir_type == "ptr");
assert!(has_ptr);
}
#[test]
fn test_ir_pattern_clone() {
let p1 = X86IRPattern::required("hello", "world");
let p2 = p1.clone();
assert_eq!(p1.description, p2.description);
assert_eq!(p1.pattern, p2.pattern);
}
#[test]
fn test_ir_gen_test_clone() {
let t1 = X86IRGenTest::new("test", X86IRGenCategory::Types, "src");
let t2 = t1.clone();
assert_eq!(t1.name, t2.name);
}
#[test]
fn test_ir_gen_test_result_debug() {
let test = X86IRGenTest::new("t", X86IRGenCategory::Types, "");
let result = X86IRGenTestResult::from_test(&test, "x", true, None, 1);
let _ = format!("{:?}", result);
}
#[test]
fn test_suite_clone() {
let suite = build_x86_ir_gen_suite();
let _ = suite.clone();
}
#[test]
fn test_runner_debug() {
let runner = X86IRGenTestRunner::new();
let _ = format!("{:?}", runner);
}
#[test]
fn test_roundtrip_tests_not_empty() {
let tests = build_roundtrip_tests();
assert!(!tests.is_empty());
assert!(
tests.len() >= 15,
"Expected at least 15 roundtrip tests, got {}",
tests.len()
);
}
#[test]
fn test_roundtrip_test_new() {
let rt = RoundtripTest::new("test", "int f() { return 0; }");
assert_eq!(rt.name, "test");
assert!(rt.ir_stable_across_roundtrip);
}
#[test]
fn test_roundtrip_test_clone() {
let rt = RoundtripTest::new("t", "int f() { return 0; }");
let rt2 = rt.clone();
assert_eq!(rt.name, rt2.name);
}
#[test]
fn test_all_roundtrip_tests_have_source() {
for test in &build_roundtrip_tests() {
assert!(!test.source.is_empty());
}
}
#[test]
fn test_all_roundtrip_tests_have_unique_names() {
let tests = build_roundtrip_tests();
let mut names = HashSet::new();
for test in &tests {
assert!(
names.insert(&test.name),
"Duplicate roundtrip test name: {}",
test.name
);
}
}
#[test]
fn test_differential_tests_not_empty() {
let tests = build_differential_tests();
assert!(!tests.is_empty());
}
#[test]
fn test_differential_test_new() {
let dt = DifferentialIRTest::new("test", "int f() { return 0; }");
assert_eq!(dt.name, "test");
assert!(dt.baseline_flags.contains(&"-O0".to_string()));
}
#[test]
fn test_differential_test_builder() {
let dt = DifferentialIRTest::new("test", "int f() { return 0; }")
.with_baseline(vec!["-O1"])
.with_compare(vec!["-O3"]);
assert!(dt.baseline_flags.contains(&"-O1".to_string()));
assert!(dt.compare_flags.contains(&"-O3".to_string()));
}
#[test]
fn test_all_differential_tests_have_source() {
for test in &build_differential_tests() {
assert!(!test.source.is_empty());
}
}
#[test]
fn test_differential_test_clone() {
let dt = DifferentialIRTest::new("d", "int f() { return 0; }");
let dt2 = dt.clone();
assert_eq!(dt.name, dt2.name);
}
#[test]
fn test_regression_registry_new() {
let registry = RegressionRegistry::new();
assert_eq!(registry.count(), 0);
}
#[test]
fn test_regression_registry_register() {
let mut registry = RegressionRegistry::new();
registry.register(RegressionTestCase::new(
"REG-TEST",
"desc",
"int f() { return 0; }",
));
assert_eq!(registry.count(), 1);
}
#[test]
fn test_regression_registry_fixed_count() {
let mut registry = RegressionRegistry::new();
registry.register(RegressionTestCase::new("A", "", "").fixed());
registry.register(RegressionTestCase::new("B", "", ""));
registry.register(RegressionTestCase::new("C", "", "").fixed());
assert_eq!(registry.fixed_count(), 2);
assert_eq!(registry.open_count(), 1);
}
#[test]
fn test_regression_registry_by_id() {
let mut registry = RegressionRegistry::new();
registry.register(RegressionTestCase::new("FIND-ME", "target", ""));
assert!(registry.by_id("FIND-ME").is_some());
assert!(registry.by_id("NONEXISTENT").is_none());
}
#[test]
fn test_build_regression_registry_not_empty() {
let registry = build_regression_registry();
assert!(registry.count() >= 5);
}
#[test]
fn test_regression_test_case_builder() {
let tc = RegressionTestCase::new("ID", "desc", "int f() { return 0; }")
.with_expected("return 0", "ret i32 0")
.with_forbidden("no mul", "mul")
.with_date("2025-12-25")
.with_issue("ISSUE-1")
.fixed();
assert_eq!(tc.expected_ir.len(), 1);
assert_eq!(tc.forbidden_ir.len(), 1);
assert_eq!(tc.date_added, "2025-12-25");
assert_eq!(tc.issue_ref, Some("ISSUE-1".to_string()));
assert!(tc.is_fixed);
}
#[test]
fn test_regression_registry_default() {
let registry: RegressionRegistry = Default::default();
assert_eq!(registry.count(), 0);
}
#[test]
fn test_delta_debugger_new() {
let dd = IRDeltaDebugger::new("int main() { return 0; }");
assert_eq!(dd.original, "int main() { return 0; }");
assert_eq!(dd.iterations, 0);
assert_eq!(dd.granularity, 2);
}
#[test]
fn test_delta_debugger_with_granularity() {
let dd = IRDeltaDebugger::new("x").with_granularity(8);
assert_eq!(dd.granularity, 8);
}
#[test]
fn test_delta_debugger_reset() {
let mut dd = IRDeltaDebugger::new("hello");
dd.current = "he".to_string();
dd.iterations = 5;
dd.reset();
assert_eq!(dd.current, "hello");
assert_eq!(dd.iterations, 0);
}
#[test]
fn test_delta_debugger_reduction_ratio() {
let mut dd = IRDeltaDebugger::new("abcdefgh");
dd.current = "abcd".to_string();
assert!((dd.reduction_ratio() - 0.5).abs() < 0.01);
}
#[test]
fn test_delta_debugger_stats() {
let mut dd = IRDeltaDebugger::new("hello world");
dd.current = "hi".to_string();
dd.iterations = 3;
let stats = dd.stats();
assert!(stats.contains("hello world"));
assert!(stats.contains("hi"));
}
#[test]
fn test_delta_debugger_empty_source() {
let dd = IRDeltaDebugger::new("");
assert_eq!(dd.reduction_ratio(), 0.0);
}
#[test]
fn test_delta_debugger_minimize_lines_no_change() {
let mut dd = IRDeltaDebugger::new("a\nb\nc");
let result = dd.minimize_lines(|s| s == "a\nb\nc");
assert_eq!(result, "a\nb\nc");
}
#[test]
fn test_delta_debugger_minimize_chars_stable() {
let mut dd = IRDeltaDebugger::new("xyz");
let result = dd.minimize_chars(|s| s.len() >= 3);
assert_eq!(result.len(), 3);
}
#[test]
fn test_delta_debugger_clone() {
let dd = IRDeltaDebugger::new("test");
let dd2 = dd.clone();
assert_eq!(dd.original, dd2.original);
}
#[test]
fn test_regression_test_case_clone() {
let tc = RegressionTestCase::new("ID", "desc", "src");
let tc2 = tc.clone();
assert_eq!(tc.id, tc2.id);
}
#[test]
fn test_batch_result_new() {
let test = X86IRGenTest::new("t", X86IRGenCategory::Types, "");
let result = X86IRGenTestResult::from_test(&test, "x", true, None, 10);
let batch = BatchVerificationResult::new("batch", vec![result.clone()]);
assert_eq!(batch.total, 1);
assert_eq!(batch.passed, 1);
assert_eq!(batch.failed, 0);
assert!(!batch.is_perfect() || batch.is_perfect());
}
#[test]
fn test_batch_result_pass_rate() {
let test1 = X86IRGenTest::new("t1", X86IRGenCategory::Types, "");
let test2 =
X86IRGenTest::new("t2", X86IRGenCategory::Types, "").require("not found", "zzz");
let r1 = X86IRGenTestResult::from_test(&test1, "x", true, None, 1);
let r2 = X86IRGenTestResult::from_test(&test2, "x", true, None, 1);
let batch = BatchVerificationResult::new("b", vec![r1, r2]);
assert!((batch.pass_rate() - 50.0).abs() < 0.01);
}
#[test]
fn test_batch_result_summary_report() {
let test = X86IRGenTest::new("t", X86IRGenCategory::Types, "");
let result = X86IRGenTestResult::from_test(&test, "x", true, None, 5);
let batch = BatchVerificationResult::new("mybatch", vec![result]);
let report = batch.summary_report();
assert!(report.contains("mybatch"));
assert!(report.contains("Total:"));
assert!(report.contains("Passed:"));
}
#[test]
fn test_batch_result_empty() {
let batch = BatchVerificationResult::new("empty", vec![]);
assert_eq!(batch.total, 0);
assert_eq!(batch.pass_rate(), 100.0);
assert!(batch.is_perfect());
}
#[test]
fn test_batch_result_clone() {
let test = X86IRGenTest::new("t", X86IRGenCategory::Types, "");
let result = X86IRGenTestResult::from_test(&test, "x", true, None, 1);
let batch = BatchVerificationResult::new("b", vec![result]);
let batch2 = batch.clone();
assert_eq!(batch.total, batch2.total);
}
#[test]
fn test_ir_comparator_new() {
let cmp = IRComparator::new();
assert!(!cmp.ignore_metadata);
assert!(!cmp.ignore_names);
}
#[test]
fn test_ir_comparator_ignore_metadata() {
let cmp = IRComparator::new().ignore_metadata();
assert!(cmp.ignore_metadata);
}
#[test]
fn test_ir_comparator_ignore_names() {
let cmp = IRComparator::new().ignore_names();
assert!(cmp.ignore_names);
}
#[test]
fn test_ir_comparator_similarity_identical() {
let cmp = IRComparator::new();
let ir = "define i32 @f() {\n ret i32 42\n}";
let score = cmp.similarity(ir, ir);
assert!((score - 1.0).abs() < 0.01);
}
#[test]
fn test_ir_comparator_similarity_different() {
let cmp = IRComparator::new();
let a = "define i32 @f() { ret i32 42 }";
let b = "define double @g() { ret double 3.14 }";
let score = cmp.similarity(a, b);
assert!(score < 0.5, "Expected low similarity, got {}", score);
}
#[test]
fn test_ir_comparator_same_function_count() {
let cmp = IRComparator::new();
let a = "define void @f() { ret void }\ndefine i32 @g() { ret i32 1 }";
let b = "define void @h() { ret void }\ndefine i32 @i() { ret i32 2 }";
assert!(cmp.same_function_count(a, b));
}
#[test]
fn test_ir_comparator_same_function_count_different() {
let cmp = IRComparator::new();
let a = "define void @f() { ret void }";
let b = "define void @g() { ret void }\ndefine void @h() { ret void }";
assert!(!cmp.same_function_count(a, b));
}
#[test]
fn test_ir_comparator_same_global_names() {
let cmp = IRComparator::new();
let a = "@x = global i32 0\n@y = global i32 1";
let b = "@y = global i32 1\n@x = global i32 0";
assert!(cmp.same_global_names(a, b));
}
#[test]
fn test_ir_comparator_same_global_names_different() {
let cmp = IRComparator::new();
let a = "@x = global i32 0";
let b = "@y = global i32 0";
assert!(!cmp.same_global_names(a, b));
}
#[test]
fn test_ir_comparator_empty_strings() {
let cmp = IRComparator::new();
assert!((cmp.similarity("", "") - 1.0).abs() < 0.01);
assert!((cmp.similarity("a", "") - 0.0).abs() < 0.01);
}
#[test]
fn test_ir_comparator_default() {
let cmp: IRComparator = Default::default();
assert!(!cmp.ignore_metadata);
assert!(!cmp.ignore_names);
assert!(!cmp.ignore_comments);
assert!(!cmp.ignore_debug_info);
}
#[test]
fn test_type_tests_cover_signed_unsigned() {
let tests = build_type_tests();
let signed_tests: Vec<_> = tests
.iter()
.filter(|t| t.name.contains("signed") || t.name.contains("unsigned"))
.collect();
assert!(signed_tests.len() >= 2);
}
#[test]
fn test_constant_tests_cover_all_folds() {
let tests = build_constant_tests();
let fold_tests: Vec<_> = tests.iter().filter(|t| t.name.contains("fold")).collect();
assert!(
fold_tests.len() >= 5,
"Expected >=5 fold tests, got {}",
fold_tests.len()
);
}
#[test]
fn test_memory_tests_cover_all_load_sizes() {
let tests = build_memory_instruction_tests();
let load_tests: Vec<_> = tests
.iter()
.filter(|t| t.name.starts_with("ir_inst_load_"))
.collect();
assert!(
load_tests.len() >= 4,
"Expected >=4 load tests, got {}",
load_tests.len()
);
}
#[test]
fn test_control_flow_tests_cover_all_loop_types() {
let tests = build_control_flow_tests();
let loop_tests: Vec<_> = tests
.iter()
.filter(|t| t.name.contains("loop") || t.name.contains("while"))
.collect();
assert!(!loop_tests.is_empty());
}
#[test]
fn test_memory_attr_tests_cover_all_ordering() {
let tests = build_memory_attr_tests();
let ordering_tests: Vec<_> = tests
.iter()
.filter(|t| t.name.contains("ordering"))
.collect();
assert!(ordering_tests.len() >= 3);
}
#[test]
fn test_function_attr_tests_cover_sanitizers() {
let tests = build_function_attr_tests();
let san: Vec<_> = tests
.iter()
.filter(|t| t.name.contains("sanitize"))
.collect();
assert!(
san.len() >= 3,
"Expected >=3 sanitizer attr tests, got {}",
san.len()
);
}
#[test]
fn test_param_attr_tests_cover_zeroext_signext() {
let tests = build_param_attr_tests();
let zx: Vec<_> = tests
.iter()
.filter(|t| t.name.contains("zeroext"))
.collect();
let sx: Vec<_> = tests
.iter()
.filter(|t| t.name.contains("signext"))
.collect();
assert!(!zx.is_empty());
assert!(!sx.is_empty());
}
#[test]
fn test_visibility_tests_cover_hidden_protected() {
let tests = build_visibility_tests();
let hidden: Vec<_> = tests.iter().filter(|t| t.name.contains("hidden")).collect();
let protected: Vec<_> = tests
.iter()
.filter(|t| t.name.contains("protected"))
.collect();
assert!(!hidden.is_empty());
assert!(!protected.is_empty());
}
#[test]
fn test_c_lang_tests_cover_static_locals() {
let tests = build_c_language_tests();
let statics: Vec<_> = tests
.iter()
.filter(|t| t.name.contains("static_local"))
.collect();
assert!(!statics.is_empty());
}
#[test]
fn test_c_lang_tests_cover_implicit_conversions() {
let tests = build_c_language_tests();
let implicit: Vec<_> = tests
.iter()
.filter(|t| t.name.contains("implicit"))
.collect();
assert!(!implicit.is_empty());
}
#[test]
fn test_all_builders_return_vecs() {
assert!(!build_type_tests().is_empty());
assert!(!build_constant_tests().is_empty());
assert!(!build_memory_instruction_tests().is_empty());
assert!(!build_arithmetic_tests().is_empty());
assert!(!build_bitwise_tests().is_empty());
assert!(!build_comparison_tests().is_empty());
assert!(!build_control_flow_tests().is_empty());
assert!(!build_aggregate_tests().is_empty());
assert!(!build_call_tests().is_empty());
assert!(!build_memory_attr_tests().is_empty());
assert!(!build_function_attr_tests().is_empty());
assert!(!build_param_attr_tests().is_empty());
assert!(!build_linkage_tests().is_empty());
assert!(!build_visibility_tests().is_empty());
assert!(!build_calling_convention_tests().is_empty());
assert!(!build_debug_info_tests().is_empty());
assert!(!build_optimization_tests().is_empty());
assert!(!build_c_language_tests().is_empty());
assert!(!build_cxx_tests().is_empty());
assert!(!build_x86_specific_tests().is_empty());
assert!(!build_stress_tests().is_empty());
assert!(!build_integration_tests().is_empty());
}
#[test]
fn test_minimum_test_counts() {
assert!(build_type_tests().len() >= 15);
assert!(build_constant_tests().len() >= 10);
assert!(build_memory_instruction_tests().len() >= 10);
assert!(build_arithmetic_tests().len() >= 10);
assert!(build_comparison_tests().len() >= 15);
assert!(build_control_flow_tests().len() >= 10);
assert!(build_c_language_tests().len() >= 15);
assert!(build_cxx_tests().len() >= 20);
assert!(build_x86_specific_tests().len() >= 20);
assert!(build_integration_tests().len() >= 3);
}
#[test]
fn test_suite_total_count_meets_minimum() {
let count = total_test_count();
assert!(count >= 150, "Expected >=150 tests, got only {}", count);
}
#[test]
fn test_intrinsic_catalog_not_empty() {
let catalog = x86_intrinsic_catalog();
assert!(!catalog.is_empty());
assert!(
catalog.len() >= 40,
"Expected >=40 intrinsics, got {}",
catalog.len()
);
}
#[test]
fn test_intrinsic_catalog_has_sse() {
let catalog = x86_intrinsic_catalog();
let sse_count = catalog.iter().filter(|i| i.isa_extension == "SSE").count();
assert!(sse_count >= 10);
}
#[test]
fn test_intrinsic_catalog_has_sse2() {
let catalog = x86_intrinsic_catalog();
let sse2_count = catalog.iter().filter(|i| i.isa_extension == "SSE2").count();
assert!(sse2_count >= 10);
}
#[test]
fn test_intrinsic_catalog_has_avx() {
let catalog = x86_intrinsic_catalog();
let avx_count = catalog.iter().filter(|i| i.isa_extension == "AVX").count();
assert!(avx_count >= 5);
}
#[test]
fn test_intrinsic_catalog_has_fma() {
let catalog = x86_intrinsic_catalog();
let fma_count = catalog.iter().filter(|i| i.isa_extension == "FMA").count();
assert!(fma_count >= 2);
}
#[test]
fn test_intrinsic_mapping_new() {
let m = X86IntrinsicMapping::new("test", "llvm.test", "ISA", "hdr.h", "desc");
assert_eq!(m.c_name, "test");
assert_eq!(m.llvm_intrinsic, "llvm.test");
assert_eq!(m.isa_extension, "ISA");
}
#[test]
fn test_intrinsic_mapping_clone() {
let m = X86IntrinsicMapping::new("a", "b", "c", "d", "e");
let m2 = m.clone();
assert_eq!(m.c_name, m2.c_name);
}
#[test]
fn test_intrinsic_catalog_all_have_headers() {
for m in &x86_intrinsic_catalog() {
assert!(
!m.header.is_empty(),
"Intrinsic '{}' has empty header",
m.c_name
);
}
}
#[test]
fn test_intrinsic_catalog_unique_names() {
let catalog = x86_intrinsic_catalog();
let mut names = HashSet::new();
for m in &catalog {
assert!(
names.insert(&m.c_name),
"Duplicate intrinsic name: {}",
m.c_name
);
}
}
#[test]
fn test_debug_info_tests_cover_dbg_declare() {
let tests = build_debug_info_tests();
let dbg: Vec<_> = tests
.iter()
.filter(|t| t.name.contains("declare"))
.collect();
assert!(!dbg.is_empty());
}
#[test]
fn test_debug_info_tests_forbid_no_g() {
let tests = build_debug_info_tests();
let no_g: Vec<_> = tests
.iter()
.filter(|t| t.name.contains("without_g"))
.collect();
assert!(!no_g.is_empty());
}
#[test]
fn test_opt_tests_cover_tail_call() {
let tests = build_optimization_tests();
let tc: Vec<_> = tests
.iter()
.filter(|t| t.name.contains("tail_call"))
.collect();
assert!(!tc.is_empty());
}
#[test]
fn test_opt_tests_cover_const_prop() {
let tests = build_optimization_tests();
let cp: Vec<_> = tests
.iter()
.filter(|t| t.name.contains("constant_propagation"))
.collect();
assert!(!cp.is_empty());
}
#[test]
fn test_opt_tests_cover_inlining() {
let tests = build_optimization_tests();
let il: Vec<_> = tests
.iter()
.filter(|t| t.name.contains("inlining"))
.collect();
assert!(!il.is_empty());
}
#[test]
fn test_opt_tests_cover_sroa() {
let tests = build_optimization_tests();
let sroa: Vec<_> = tests.iter().filter(|t| t.name.contains("sroa")).collect();
assert!(!sroa.is_empty());
}
#[test]
fn test_opt_tests_cover_all_O_levels() {
let tests = build_optimization_tests();
let o0: Vec<_> = tests.iter().filter(|t| t.name.contains("O0")).collect();
let o1: Vec<_> = tests.iter().filter(|t| t.name.contains("O1")).collect();
let o2: Vec<_> = tests.iter().filter(|t| t.name.contains("O2")).collect();
assert!(!o0.is_empty() || !o1.is_empty());
assert!(!o2.is_empty());
}
#[test]
fn test_x86_tests_cover_stack_protector() {
let tests = build_x86_specific_tests();
let sp: Vec<_> = tests
.iter()
.filter(|t| t.name.contains("stack") && t.name.contains("protect"))
.collect();
assert!(!sp.is_empty());
}
#[test]
fn test_x86_tests_cover_fences() {
let tests = build_x86_specific_tests();
let fences: Vec<_> = tests
.iter()
.filter(|t| {
t.name.contains("fence")
|| t.name.contains("mfence")
|| t.name.contains("lfence")
|| t.name.contains("sfence")
})
.collect();
assert!(!fences.is_empty());
}
#[test]
fn test_x86_tests_cover_cache_ops() {
let tests = build_x86_specific_tests();
let cache: Vec<_> = tests
.iter()
.filter(|t| t.name.contains("clflush") || t.name.contains("prefetch"))
.collect();
assert!(cache.len() >= 2);
}
#[test]
fn test_cxx_tests_cover_eh_all_ops() {
let tests = build_cxx_tests();
assert!(tests.iter().any(|t| t.name.contains("landingpad")));
assert!(tests.iter().any(|t| t.name.contains("invoke")));
assert!(tests.iter().any(|t| t.name.contains("resume")));
}
#[test]
fn test_cxx_tests_cover_member_pointers() {
let tests = build_cxx_tests();
let mp: Vec<_> = tests
.iter()
.filter(|t| t.name.contains("member_ptr"))
.collect();
assert!(mp.len() >= 2);
}
#[test]
fn test_cxx_tests_cover_lambda_captures() {
let tests = build_cxx_tests();
let lambdas: Vec<_> = tests.iter().filter(|t| t.name.contains("lambda")).collect();
assert!(
lambdas.len() >= 3,
"Expected >=3 lambda tests, got {}",
lambdas.len()
);
}
#[test]
fn test_stress_tests_count() {
let tests = build_stress_tests();
assert!(
tests.len() >= 10,
"Expected >=10 stress tests, got {}",
tests.len()
);
}
#[test]
fn test_all_tests_have_nonempty_source() {
let suite = build_x86_ir_gen_suite();
for test in &suite.tests {
assert!(
!test.source.is_empty(),
"Test '{}' has empty source",
test.name
);
}
}
#[test]
fn test_all_tests_have_valid_category() {
let suite = build_x86_ir_gen_suite();
for test in &suite.tests {
let s = test.category.as_str();
assert!(!s.is_empty());
}
}
#[test]
fn test_runner_single_result_has_name() {
let runner = X86IRGenTestRunner::new();
let test = X86IRGenTest::new("identity_test", X86IRGenCategory::Types, "");
let result = runner.run_single(&test);
assert_eq!(result.name, "identity_test");
}
#[test]
fn test_runner_result_has_duration() {
let runner = X86IRGenTestRunner::new();
let test = X86IRGenTest::new("timed", X86IRGenCategory::Types, "");
let result = runner.run_single(&test);
assert!(result.duration_ms < 10000); }
#[test]
fn test_edge_case_generator_new() {
let gen = EdgeCaseGenerator::new();
assert_eq!(gen.tests_generated, 0);
assert!(gen.config.include_overflow);
}
#[test]
fn test_edge_case_generator_overflow_tests() {
let gen = EdgeCaseGenerator::new();
let tests = gen.gen_overflow_tests();
assert!(!tests.is_empty());
assert!(tests.len() >= 3);
}
#[test]
fn test_edge_case_generator_div_zero_tests() {
let gen = EdgeCaseGenerator::new();
let tests = gen.gen_div_zero_tests();
assert!(!tests.is_empty());
}
#[test]
fn test_edge_case_generator_null_ptr_tests() {
let gen = EdgeCaseGenerator::new();
let tests = gen.gen_null_ptr_tests();
assert!(!tests.is_empty());
}
#[test]
fn test_edge_case_generator_uninit_tests() {
let gen = EdgeCaseGenerator::new();
let tests = gen.gen_uninit_tests();
assert!(!tests.is_empty());
}
#[test]
fn test_edge_case_generator_type_pun_tests() {
let gen = EdgeCaseGenerator::new();
let tests = gen.gen_type_pun_tests();
assert!(!tests.is_empty());
}
#[test]
fn test_edge_case_generator_large_stack_tests() {
let gen = EdgeCaseGenerator::new();
let tests = gen.gen_large_stack_tests();
assert!(!tests.is_empty());
}
#[test]
fn test_edge_case_generator_generate_all() {
let mut gen = EdgeCaseGenerator::new();
let tests = gen.generate_all();
assert!(!tests.is_empty());
assert!(gen.tests_generated > 0);
assert_eq!(gen.tests_generated, tests.len());
}
#[test]
fn test_edge_case_generator_disabled_overflow() {
let config = EdgeCaseConfig {
include_overflow: false,
..Default::default()
};
let gen = EdgeCaseGenerator::new().with_config(config);
let tests = gen.gen_overflow_tests();
assert!(tests.is_empty());
}
#[test]
fn test_edge_case_generator_disabled_div_zero() {
let config = EdgeCaseConfig {
include_div_zero: false,
..Default::default()
};
let gen = EdgeCaseGenerator::new().with_config(config);
let tests = gen.gen_div_zero_tests();
assert!(tests.is_empty());
}
#[test]
fn test_edge_case_generator_disabled_all() {
let config = EdgeCaseConfig {
include_overflow: false,
include_div_zero: false,
include_null_ptr: false,
include_uninit: false,
include_type_pun: false,
include_large_stack: false,
};
let mut gen = EdgeCaseGenerator::new().with_config(config);
let tests = gen.generate_all();
assert!(tests.is_empty());
}
#[test]
fn test_edge_case_config_default() {
let config = EdgeCaseConfig::default();
assert!(config.include_overflow);
assert!(config.include_div_zero);
assert!(config.include_null_ptr);
assert!(config.include_uninit);
assert!(config.include_type_pun);
assert!(config.include_large_stack);
}
#[test]
fn test_edge_case_generator_default() {
let gen: EdgeCaseGenerator = Default::default();
assert_eq!(gen.tests_generated, 0);
}
#[test]
fn test_edge_case_generator_clone() {
let gen = EdgeCaseGenerator::new();
let gen2 = gen.clone();
assert_eq!(gen.config.include_overflow, gen2.config.include_overflow);
}
#[test]
fn test_ir_instruction_coverage_basic() {
let suite = build_x86_ir_gen_suite();
let all_names: HashSet<_> = suite.tests.iter().map(|t| &t.name).collect();
assert!(
all_names.iter().any(|n| n.contains("alloca")),
"Missing alloca test"
);
assert!(
all_names.iter().any(|n| n.contains("load")),
"Missing load test"
);
assert!(
all_names.iter().any(|n| n.contains("store")),
"Missing store test"
);
assert!(
all_names.iter().any(|n| n.contains("gep")),
"Missing GEP test"
);
assert!(
all_names.iter().any(|n| n.contains("br")),
"Missing branch test"
);
assert!(
all_names.iter().any(|n| n.contains("switch")),
"Missing switch test"
);
assert!(
all_names.iter().any(|n| n.contains("phi")),
"Missing phi test"
);
assert!(
all_names.iter().any(|n| n.contains("select")),
"Missing select test"
);
}
#[test]
fn test_ir_arith_coverage() {
let suite = build_x86_ir_gen_suite();
let all_names: HashSet<_> = suite.tests.iter().map(|t| &t.name).collect();
assert!(
all_names.iter().any(|n| n.contains("add")),
"Missing add test"
);
assert!(
all_names.iter().any(|n| n.contains("sub")),
"Missing sub test"
);
assert!(
all_names.iter().any(|n| n.contains("mul")),
"Missing mul test"
);
assert!(
all_names.iter().any(|n| n.contains("sdiv")),
"Missing sdiv test"
);
assert!(
all_names.iter().any(|n| n.contains("udiv")),
"Missing udiv test"
);
}
#[test]
fn test_ir_type_coverage_scalar() {
let suite = build_x86_ir_gen_suite();
let all_names: HashSet<_> = suite.tests.iter().map(|t| &t.name).collect();
let types = ["void", "i1", "i8", "i16", "i32", "i64", "float", "double"];
for typ in &types {
assert!(
all_names.iter().any(|n| n.contains(typ)),
"Missing type coverage for: {}",
typ
);
}
}
#[test]
fn test_ir_coverage_calling_conventions() {
let suite = build_x86_ir_gen_suite();
let all_names: HashSet<_> = suite.tests.iter().map(|t| &t.name).collect();
let ccs = [
"ccc",
"fastcc",
"coldcc",
"x86_stdcallcc",
"x86_fastcallcc",
"x86_thiscallcc",
"x86_vectorcallcc",
"x86_regcallcc",
];
for cc in &ccs {
assert!(
all_names.iter().any(|n| n.contains(cc)),
"Missing calling convention coverage for: {}",
cc
);
}
}
#[test]
fn test_ir_coverage_linkage_types() {
let suite = build_x86_ir_gen_suite();
let all_names: HashSet<_> = suite.tests.iter().map(|t| &t.name).collect();
let linkages = [
"external",
"internal",
"private",
"weak",
"linkonce",
"common",
"extern_weak",
];
for linkage in &linkages {
assert!(
all_names.iter().any(|n| n.contains(linkage)),
"Missing linkage coverage for: {}",
linkage
);
}
}
#[test]
fn test_ir_coverage_visibility() {
let suite = build_x86_ir_gen_suite();
let all_names: HashSet<_> = suite.tests.iter().map(|t| &t.name).collect();
assert!(
all_names.iter().any(|n| n.contains("hidden")),
"Missing hidden visibility test"
);
assert!(
all_names.iter().any(|n| n.contains("protected")),
"Missing protected visibility test"
);
}
#[test]
fn test_ir_coverage_x86_vector_sizes() {
let features = build_x86_specific_tests();
let names: Vec<_> = features.iter().map(|t| &t.name).collect();
assert!(
names
.iter()
.any(|n| n.contains("<2 x i64>") || n.contains("v2i64")),
"Missing 2 x i64 vector test"
);
assert!(
names
.iter()
.any(|n| n.contains("<4 x i32>") || n.contains("v4i32")),
"Missing 4 x i32 vector test"
);
assert!(
names
.iter()
.any(|n| n.contains("<8 x i16>") || n.contains("v8i16")),
"Missing 8 x i16 vector test"
);
assert!(
names
.iter()
.any(|n| n.contains("<16 x i8>") || n.contains("v16i8")),
"Missing 16 x i8 vector test"
);
}
#[test]
fn test_all_tests_have_valid_target_triple() {
let suite = build_x86_ir_gen_suite();
for test in &suite.tests {
assert!(
!test.target_triple.is_empty(),
"Test '{}' has empty target triple",
test.name
);
}
}
#[test]
fn test_all_required_patterns_are_checkable() {
let suite = build_x86_ir_gen_suite();
for test in &suite.tests {
for pat in &test.required_ir {
assert!(
pat.min_count <= 1000,
"Pattern '{}' in test '{}' has unreasonably high min_count: {}",
pat.description,
test.name,
pat.min_count
);
}
}
}
#[test]
fn test_suite_opt_levels_are_valid() {
let suite = build_x86_ir_gen_suite();
for test in &suite.tests {
match test.opt_level {
X86OptLevel::O0
| X86OptLevel::O1
| X86OptLevel::O2
| X86OptLevel::O3
| X86OptLevel::Os
| X86OptLevel::Oz => {}
}
}
}
#[test]
fn test_discover_categories_via_iteration() {
let suite = build_x86_ir_gen_suite();
let categories: HashSet<X86IRGenCategory> =
suite.tests.iter().map(|t| t.category).collect();
assert!(
categories.len() >= 15,
"Expected >=15 categories, got {}",
categories.len()
);
}
#[test]
fn test_batch_result_count_by_category() {
let suite = build_x86_ir_gen_suite();
let test = X86IRGenTest::new("cat_test", X86IRGenCategory::Types, "");
let result = X86IRGenTestResult::from_test(&test, "x", true, None, 1);
let mut batch = BatchVerificationResult::new("cat_batch", vec![result]);
batch.count_by_category(&suite);
}
#[test]
fn test_ir_comparator_similarity_edge_cases() {
let cmp = IRComparator::new();
let a = "define void @f() { ret void }";
let b = "define void @f() {\n ret void\n}";
let score = cmp.similarity(a, b);
assert!(score > 0.0, "Expected non-zero similarity");
}
#[test]
fn test_differential_tests_unique_names() {
let tests = build_differential_tests();
let mut names = HashSet::new();
for t in &tests {
assert!(
names.insert(&t.name),
"Duplicate differential test name: {}",
t.name
);
}
}
#[test]
fn test_regression_tests_unique_ids() {
let registry = build_regression_registry();
let mut ids = HashSet::new();
for t in ®istry.tests {
assert!(ids.insert(&t.id), "Duplicate regression test ID: {}", t.id);
}
}
#[test]
fn test_delta_debugger_reduction_ratio_bound() {
let dd = IRDeltaDebugger::new("test");
let ratio = dd.reduction_ratio();
assert!(
ratio >= 0.0 && ratio <= 1.0,
"Reduction ratio {} out of [0,1]",
ratio
);
}
#[test]
fn test_suite_repeated_builds_consistent() {
for _ in 0..10 {
let count = build_x86_ir_gen_suite().len();
assert!(count > 50);
}
}
#[test]
fn test_pattern_no_panic_on_empty_text() {
let pat = X86IRPattern::required("test", "hello");
let count = pat.count_in("");
assert_eq!(count, 0);
assert!(!pat.check(""));
}
#[test]
fn test_pattern_no_panic_on_single_char() {
let pat = X86IRPattern::required("test", "a");
assert_eq!(pat.count_in("a"), 1);
assert_eq!(pat.count_in("b"), 0);
}
#[test]
fn test_pattern_no_panic_on_unicode() {
let pat = X86IRPattern::required("test", "hello");
let count = pat.count_in("héllo wörld");
assert_eq!(count, 0);
}
#[test]
fn test_runner_results_ordered() {
let suite = build_x86_ir_gen_suite();
let runner = X86IRGenTestRunner::new();
let results = runner.run_suite(&suite);
assert_eq!(results.len(), suite.len());
for (i, result) in results.iter().enumerate() {
assert_eq!(result.name, suite.tests[i].name);
}
}
#[test]
fn test_category_summary_every_category_counts() {
let summary = category_summary();
let total: usize = summary.values().sum();
assert_eq!(total, total_test_count());
}
#[test]
fn test_x86_intrinsic_catalog_llvm_prefix() {
for m in &x86_intrinsic_catalog() {
assert!(
m.llvm_intrinsic.starts_with("llvm."),
"Intrinsic '{}' does not start with llvm.",
m.llvm_intrinsic
);
}
}
#[test]
fn test_edge_case_generator_produces_valid_tests() {
let mut gen = EdgeCaseGenerator::new();
let tests = gen.generate_all();
for test in &tests {
assert!(!test.name.is_empty());
assert!(!test.source.is_empty());
assert!(test.should_compile);
}
}
#[test]
fn test_batch_verification_empty() {
let batch = BatchVerificationResult::new("empty", vec![]);
assert!(batch.is_perfect());
assert!((batch.pass_rate() - 100.0).abs() < 0.01);
let report = batch.summary_report();
assert!(report.contains("empty"));
assert!(report.contains("Total:"));
}
#[test]
fn test_ir_comparator_full_config() {
let cmp = IRComparator {
ignore_metadata: true,
ignore_names: true,
ignore_comments: true,
ignore_debug_info: true,
};
let a = "define void @f() {\n ; comment\n ret void\n}";
let b = "define void @f() { ret void }";
let score = cmp.similarity(a, b);
assert!(score > 0.0, "Expected similarity with all ignore flags");
}
#[test]
fn test_type_map_coverage_complete() {
let map = x86_type_map();
let types: HashSet<&str> = map.iter().map(|t| t.ir_type.as_str()).collect();
assert!(types.contains("void"));
assert!(types.contains("i1"));
assert!(types.contains("i8"));
assert!(types.contains("i16"));
assert!(types.contains("i32"));
assert!(types.contains("i64"));
assert!(types.contains("float"));
assert!(types.contains("double"));
assert!(types.contains("x86_fp80"));
assert!(types.contains("ptr"));
assert!(types.contains("<4 x i32>"));
}
#[test]
fn test_intrinsic_catalog_isa_extensions() {
let catalog = x86_intrinsic_catalog();
let mut isas = HashSet::new();
for m in &catalog {
isas.insert(m.isa_extension.as_str());
}
assert!(isas.contains("SSE"));
assert!(isas.contains("SSE2"));
assert!(isas.contains("SSE3"));
assert!(isas.contains("SSSE3"));
assert!(isas.contains("SSE4.1"));
assert!(isas.contains("AVX"));
assert!(isas.contains("AVX2"));
assert!(isas.contains("FMA"));
}
#[test]
fn test_full_suite_consistency() {
let suite = build_x86_ir_gen_suite();
let total = suite.len();
let total2 = total_test_count();
assert_eq!(total, total2, "Suite len and test count must match");
let summary = category_summary();
let cat_total: usize = summary.values().sum();
assert_eq!(cat_total, total, "Category summary must sum to total");
}
#[test]
fn test_edge_case_all_tests_should_compile() {
let mut gen = EdgeCaseGenerator::new();
let tests = gen.generate_all();
for test in &tests {
assert!(
test.should_compile,
"Edge test '{}' should compile by default",
test.name
);
}
}
#[test]
fn test_delta_debugger_minimize_lines_reduces_size() {
let mut dd = IRDeltaDebugger::new("line1\nline2\nline3\nline4\nline5");
let result = dd.minimize_lines(|s| s.contains("line1"));
assert!(result.contains("line1"));
assert!(result.len() <= dd.original.len());
}
#[test]
fn test_verify_no_duplicate_pub_functions() {
let suite1 = build_x86_ir_gen_suite();
let suite2 = build_x86_ir_gen_suite();
assert_eq!(suite1.len(), suite2.len());
assert_eq!(total_test_count(), suite1.len());
}
}