use crate::clang::clang_x86_pipeline::{
compile_to_x86_assembly, compile_to_x86_ir, compile_to_x86_object, compile_with_options,
PipelineResult, PipelineStage, StageResult, X86CompileOptions, X86IRGenerator, X86OptLevel,
X86OutputFormat, X86Pipeline,
};
use crate::clang::{
compile_c, compile_c_file, compile_c_string, compile_c_to_assembly, CLangStandard,
ClangCodeGen, ClangDriver, ClangOptions, ClangTargetInfo,
};
use crate::x86::{
x86_calling_convention::{X86ArgClass, X86ArgInfo, X86CallFrame, X86CallingConvention},
x86_frame_lowering::{CallConv, X86FrameInfo, X86FrameLowering},
x86_full_mc_decoder::X86FullMCDecoder,
x86_full_mc_encoder::X86FullMCEncoder,
x86_instr_info::{
OperandType, X86InstrDesc, X86InstrInfo, X86MemOperand, X86Opcode, X86Operand,
},
x86_isel::X86InstructionSelector,
x86_mc_decoder::X86MCDecoder,
x86_mc_encoder::X86MCEncoder,
x86_register_info::{X86RegisterInfo, X86_32_REG_COUNT, X86_64_REG_COUNT},
x86_subtarget::X86Subtarget,
x86_target_machine::X86TargetMachine,
};
use crate::{
basic_block::new_basic_block,
context::LLVMContext,
function::{new_function, Function},
instruction::Opcode,
ir_builder::IRBuilder,
module::Module,
types::{Type, TypeKind},
value::{Value, ValueRef},
verifier::{verify_function, verify_module, VerifierResult},
};
use std::collections::{BTreeMap, HashMap, HashSet, VecDeque};
use std::fmt;
use std::io::Write;
use std::path::{Path, PathBuf};
use std::process::{Command, Output, Stdio};
use std::sync::atomic::{AtomicBool, AtomicU64, AtomicUsize, Ordering};
use std::sync::{Arc, Mutex, RwLock};
use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum X86TestCategory {
IntegerOps,
FloatingPoint,
Pointers,
Aggregates,
ControlFlow,
Functions,
CPlusPlusFeatures,
X86Builtins,
ABITests,
OptimizationTests,
DebugInfoTests,
InlineAssembly,
VectorOps,
MemoryOrdering,
ExceptionHandling,
ThreadLocalStorage,
Linkage,
Attributes,
Sanitizers,
Profiling,
LTO,
Roundtrip,
Differential,
StressTest,
FuzzTarget,
Regression,
}
impl fmt::Display for X86TestCategory {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::IntegerOps => write!(f, "integer-ops"),
Self::FloatingPoint => write!(f, "floating-point"),
Self::Pointers => write!(f, "pointers"),
Self::Aggregates => write!(f, "aggregates"),
Self::ControlFlow => write!(f, "control-flow"),
Self::Functions => write!(f, "functions"),
Self::CPlusPlusFeatures => write!(f, "c++-features"),
Self::X86Builtins => write!(f, "x86-builtins"),
Self::ABITests => write!(f, "abi-tests"),
Self::OptimizationTests => write!(f, "optimization-tests"),
Self::DebugInfoTests => write!(f, "debug-info"),
Self::InlineAssembly => write!(f, "inline-assembly"),
Self::VectorOps => write!(f, "vector-ops"),
Self::MemoryOrdering => write!(f, "memory-ordering"),
Self::ExceptionHandling => write!(f, "exception-handling"),
Self::ThreadLocalStorage => write!(f, "thread-local-storage"),
Self::Linkage => write!(f, "linkage"),
Self::Attributes => write!(f, "attributes"),
Self::Sanitizers => write!(f, "sanitizers"),
Self::Profiling => write!(f, "profiling"),
Self::LTO => write!(f, "lto"),
Self::Roundtrip => write!(f, "roundtrip"),
Self::Differential => write!(f, "differential"),
Self::StressTest => write!(f, "stress-test"),
Self::FuzzTarget => write!(f, "fuzz-target"),
Self::Regression => write!(f, "regression"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86VerificationTarget {
IR,
Assembly,
ObjectFile,
Roundtrip,
Differential,
OptimizationPass,
DebugInfo,
FullPipeline,
}
impl fmt::Display for X86VerificationTarget {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::IR => write!(f, "IR"),
Self::Assembly => write!(f, "Assembly"),
Self::ObjectFile => write!(f, "ObjectFile"),
Self::Roundtrip => write!(f, "Roundtrip"),
Self::Differential => write!(f, "Differential"),
Self::OptimizationPass => write!(f, "OptimizationPass"),
Self::DebugInfo => write!(f, "DebugInfo"),
Self::FullPipeline => write!(f, "FullPipeline"),
}
}
}
#[derive(Debug, Clone)]
pub struct X86CodeGenTest {
pub name: String,
pub category: X86TestCategory,
pub source: String,
pub expected_return: Option<i32>,
pub expected_stdout: Option<String>,
pub expected_stderr: Option<String>,
pub flags: Vec<String>,
pub verification_targets: Vec<X86VerificationTarget>,
pub expected_ir: Vec<String>,
pub unexpected_ir: Vec<String>,
pub expected_asm: Vec<String>,
pub unexpected_asm: Vec<String>,
pub should_compile: bool,
pub is_xfail: bool,
pub xfail_reason: Option<String>,
pub required_features: Vec<String>,
pub target_triple: Option<String>,
pub opt_level: X86OptLevel,
pub priority: u8,
pub tags: Vec<String>,
pub time_budget_ms: Option<u64>,
pub size_budget_bytes: Option<u64>,
}
impl X86CodeGenTest {
pub fn new(name: &str, category: X86TestCategory, source: &str) -> Self {
Self {
name: name.to_string(),
category,
source: source.to_string(),
expected_return: None,
expected_stdout: None,
expected_stderr: None,
flags: Vec::new(),
verification_targets: vec![X86VerificationTarget::FullPipeline],
expected_ir: Vec::new(),
unexpected_ir: Vec::new(),
expected_asm: Vec::new(),
unexpected_asm: Vec::new(),
should_compile: true,
is_xfail: false,
xfail_reason: None,
required_features: Vec::new(),
target_triple: None,
opt_level: X86OptLevel::O0,
priority: 3,
tags: Vec::new(),
time_budget_ms: None,
size_budget_bytes: None,
}
}
pub fn with_return(mut self, ret: i32) -> Self {
self.expected_return = Some(ret);
self
}
pub fn with_stdout(mut self, stdout: &str) -> Self {
self.expected_stdout = Some(stdout.to_string());
self
}
pub fn with_flags(mut self, flags: &[&str]) -> Self {
self.flags = flags.iter().map(|s| s.to_string()).collect();
self
}
pub fn with_expected_ir(mut self, pattern: &str) -> Self {
self.expected_ir.push(pattern.to_string());
self
}
pub fn with_unexpected_ir(mut self, pattern: &str) -> Self {
self.unexpected_ir.push(pattern.to_string());
self
}
pub fn with_expected_asm(mut self, pattern: &str) -> Self {
self.expected_asm.push(pattern.to_string());
self
}
pub fn xfail(mut self, reason: &str) -> Self {
self.is_xfail = true;
self.xfail_reason = Some(reason.to_string());
self
}
pub fn with_opt_level(mut self, level: X86OptLevel) -> Self {
self.opt_level = level;
self
}
pub fn with_verification(mut self, target: X86VerificationTarget) -> Self {
self.verification_targets.push(target);
self
}
pub fn with_verifications(mut self, targets: &[X86VerificationTarget]) -> Self {
self.verification_targets = targets.to_vec();
self
}
pub fn with_priority(mut self, priority: u8) -> Self {
self.priority = priority.min(5).max(1);
self
}
pub fn with_tags(mut self, tags: &[&str]) -> Self {
self.tags = tags.iter().map(|s| s.to_string()).collect();
self
}
pub fn with_features(mut self, features: &[&str]) -> Self {
self.required_features = features.iter().map(|s| s.to_string()).collect();
self
}
pub fn with_time_budget(mut self, ms: u64) -> Self {
self.time_budget_ms = Some(ms);
self
}
pub fn with_size_budget(mut self, bytes: u64) -> Self {
self.size_budget_bytes = Some(bytes);
self
}
pub fn expect_compiles(&self) -> bool {
self.should_compile
}
pub fn effective_triple(&self) -> String {
self.target_triple
.clone()
.unwrap_or_else(|| "x86_64-unknown-linux-gnu".to_string())
}
}
#[derive(Debug)]
pub struct X86CodeGenTestSuite {
pub name: String,
pub description: String,
pub tests: Vec<X86CodeGenTest>,
pub setup: Option<fn() -> bool>,
pub teardown: Option<fn()>,
pub tags: Vec<String>,
}
impl X86CodeGenTestSuite {
pub fn new(name: &str, description: &str) -> Self {
Self {
name: name.to_string(),
description: description.to_string(),
tests: Vec::new(),
setup: None,
teardown: None,
tags: Vec::new(),
}
}
pub fn add(&mut self, test: X86CodeGenTest) -> &mut Self {
self.tests.push(test);
self
}
pub fn add_many(&mut self, tests: Vec<X86CodeGenTest>) -> &mut Self {
self.tests.extend(tests);
self
}
pub fn len(&self) -> usize {
self.tests.len()
}
pub fn is_empty(&self) -> bool {
self.tests.is_empty()
}
pub fn filter_by_tag(&self, tag: &str) -> Vec<&X86CodeGenTest> {
self.tests
.iter()
.filter(|t| t.tags.iter().any(|t2| t2 == tag))
.collect()
}
pub fn filter_by_category(&self, category: X86TestCategory) -> Vec<&X86CodeGenTest> {
self.tests
.iter()
.filter(|t| t.category == category)
.collect()
}
}
#[derive(Debug, Clone)]
pub struct X86VerificationResult {
pub name: String,
pub passed: bool,
pub duration: Duration,
pub messages: Vec<String>,
pub ir_output: Option<String>,
pub asm_output: Option<String>,
pub binary_size: Option<usize>,
pub compilation_success: bool,
pub compiler_output: Vec<String>,
}
impl X86VerificationResult {
pub fn pass(name: &str) -> Self {
Self {
name: name.to_string(),
passed: true,
duration: Duration::ZERO,
messages: vec!["All checks passed.".to_string()],
ir_output: None,
asm_output: None,
binary_size: None,
compilation_success: true,
compiler_output: Vec::new(),
}
}
pub fn fail(name: &str, reason: &str) -> Self {
Self {
name: name.to_string(),
passed: false,
duration: Duration::ZERO,
messages: vec![format!("FAIL: {}", reason)],
ir_output: None,
asm_output: None,
binary_size: None,
compilation_success: false,
compiler_output: Vec::new(),
}
}
pub fn is_pass(&self) -> bool {
self.passed
}
pub fn compiled(&self) -> bool {
self.compilation_success
}
}
pub struct X86CodeGenVerifier {
pub options: X86CompileOptions,
pub verify_ir: bool,
pub verify_asm: bool,
pub verify_obj: bool,
pub do_roundtrip: bool,
pub do_differential: bool,
pub system_cc: Option<String>,
pub verbose: bool,
pub results: Vec<X86VerificationResult>,
pub total_checks: usize,
pub failed_checks: usize,
}
impl X86CodeGenVerifier {
pub fn new() -> Self {
Self {
options: X86CompileOptions::default(),
verify_ir: true,
verify_asm: true,
verify_obj: true,
do_roundtrip: false,
do_differential: false,
system_cc: None,
verbose: false,
results: Vec::new(),
total_checks: 0,
failed_checks: 0,
}
}
pub fn full_verification(mut self) -> Self {
self.verify_ir = true;
self.verify_asm = true;
self.verify_obj = true;
self.do_roundtrip = true;
self
}
pub fn with_system_cc(mut self, cc: &str) -> Self {
self.system_cc = Some(cc.to_string());
self.do_differential = true;
self
}
pub fn verbose(mut self) -> Self {
self.verbose = true;
self
}
pub fn with_options(mut self, opts: X86CompileOptions) -> Self {
self.options = opts;
self
}
pub fn verify(&mut self, test: &X86CodeGenTest) -> X86VerificationResult {
let start = Instant::now();
let mut result = X86VerificationResult::pass(&test.name);
if let Ok(ir) = self.compile_to_ir(&test.source) {
result.ir_output = Some(ir.clone());
if self.verify_ir {
let ir_ok = self.verify_ir_format(&ir, test);
if !ir_ok {
result.passed = false;
result.messages.push("IR verification failed.".to_string());
}
}
} else {
result.compilation_success = false;
result.passed = false;
result
.messages
.push("Compilation to IR failed.".to_string());
}
if result.compilation_success {
if let Ok(asm) = self.compile_to_asm(&test.source) {
result.asm_output = Some(asm.clone());
if self.verify_asm {
let asm_ok = self.verify_assembly(&asm, test);
if !asm_ok {
result.passed = false;
result
.messages
.push("Assembly verification failed.".to_string());
}
}
}
}
if self.do_roundtrip && result.compilation_success {
let rt_ok = self.roundtrip_verify(&test.source);
if !rt_ok {
result.passed = false;
result
.messages
.push("Roundtrip verification failed.".to_string());
}
}
if self.do_differential && result.compilation_success {
let diff_ok = self.differential_verify(&test.source);
if !diff_ok {
result.passed = false;
result
.messages
.push("Differential verification failed.".to_string());
}
}
result.duration = start.elapsed();
self.total_checks += 1;
if !result.passed {
self.failed_checks += 1;
}
self.results.push(result.clone());
result
}
pub fn verify_ir_format(&self, ir: &str, test: &X86CodeGenTest) -> bool {
let mut ok = true;
for pattern in &test.expected_ir {
if !ir.contains(pattern.as_str()) {
if self.verbose {
eprintln!(" IR missing expected pattern: {}", pattern);
}
ok = false;
}
}
for pattern in &test.unexpected_ir {
if ir.contains(pattern.as_str()) {
if self.verbose {
eprintln!(" IR contains unexpected pattern: {}", pattern);
}
ok = false;
}
}
if !ir.contains("define") && !ir.trim().is_empty() {
ok = false;
}
let has_well_formed_ir = ir.contains("target") || ir.contains("%");
if !has_well_formed_ir && !ir.trim().is_empty() {
if self.verbose {
eprintln!(" IR may not be well-formed (no target or value references)");
}
}
ok
}
pub fn verify_assembly(&self, asm: &str, test: &X86CodeGenTest) -> bool {
let mut ok = true;
for pattern in &test.expected_asm {
if !asm.contains(pattern.as_str()) {
if self.verbose {
eprintln!(" ASM missing expected pattern: {}", pattern);
}
ok = false;
}
}
for pattern in &test.unexpected_asm {
if asm.contains(pattern.as_str()) {
if self.verbose {
eprintln!(" ASM contains unexpected pattern: {}", pattern);
}
ok = false;
}
}
let is_x86_asm = asm.contains(".text")
|| asm.contains(".section")
|| asm.contains(".globl")
|| asm.contains(".globl")
|| asm.contains("mov")
|| asm.contains("add")
|| asm.contains("sub")
|| asm.contains("ret");
if !is_x86_asm && !asm.trim().is_empty() {
if self.verbose {
eprintln!(" ASM may not be valid x86 assembly");
}
ok = false;
}
ok
}
pub fn roundtrip_verify(&self, source: &str) -> bool {
let obj1 = match self.compile_to_obj(source) {
Ok(o) => o,
Err(_) => return false,
};
let obj2 = match self.compile_to_obj(source) {
Ok(o) => o,
Err(_) => return false,
};
if obj1.len() != obj2.len() {
if self.verbose {
eprintln!(
" Roundtrip: object sizes differ: {} vs {}",
obj1.len(),
obj2.len()
);
}
return false;
}
let match_count = obj1.iter().zip(&obj2).filter(|(a, b)| a == b).count();
let match_ratio = match_count as f64 / obj1.len() as f64;
if match_ratio < 0.99 {
if self.verbose {
eprintln!(
" Roundtrip: object files differ (match ratio: {:.2}%)",
match_ratio * 100.0
);
}
return false;
}
true
}
pub fn differential_verify(&self, source: &str) -> bool {
let system_cc = match &self.system_cc {
Some(cc) => cc.clone(),
None => {
if Self::find_executable("gcc") {
"gcc".to_string()
} else if Self::find_executable("clang") {
"clang".to_string()
} else {
return true;
}
}
};
let temp_dir = match std::env::temp_dir().to_str() {
Some(d) => d.to_string(),
None => return true, };
let src_path = format!("{}/x86_diff_test_{}.c", temp_dir, Self::timestamp_ms());
let obj_path_a = format!("{}/x86_diff_a_{}.o", temp_dir, Self::timestamp_ms());
let obj_path_b = format!("{}/x86_diff_b_{}.o", temp_dir, Self::timestamp_ms());
if let Err(_) = std::fs::write(&src_path, source) {
return true; }
let tmp_our = std::env::temp_dir().join(format!("x86_diff_our_{}.o", Self::timestamp_ms()));
let our_result = compile_to_x86_object(source, &tmp_our);
let our_success = our_result.map(|r| r.success).unwrap_or(false);
let _ = std::fs::remove_file(&tmp_our);
let sys_result = Command::new(&system_cc)
.args(["-x", "c", "-c", &src_path, "-o", &obj_path_a])
.output();
let sys_success = sys_result
.as_ref()
.map(|o| o.status.success())
.unwrap_or(false);
if our_success != sys_success {
if self.verbose {
eprintln!(
" Differential: our compiler {}, system {}",
if our_success { "passed" } else { "failed" },
if sys_success { "passed" } else { "failed" }
);
}
return false;
}
if our_success && sys_success {
if let Ok(sys_obj) = std::fs::read(&obj_path_a) {
if let Ok(our_obj) = self.compile_to_obj(source) {
let size_ratio = our_obj.len() as f64 / sys_obj.len() as f64;
if size_ratio < 0.5 || size_ratio > 2.0 {
if self.verbose {
eprintln!(
" Differential: object size differs significantly (ratio: {:.2})",
size_ratio
);
}
return false;
}
}
}
}
let _ = std::fs::remove_file(&src_path);
let _ = std::fs::remove_file(&obj_path_a);
let _ = std::fs::remove_file(&obj_path_b);
true
}
fn compile_to_ir(&self, source: &str) -> Result<String, String> {
let mut opts = self.options.clone();
opts.output_format = X86OutputFormat::LLVMIR;
opts.stop_after = Some(PipelineStage::IRGen);
match compile_with_options(opts, source) {
Ok(result) if result.success => {
result.text_output.ok_or_else(|| "No IR output".to_string())
}
Ok(_) => Err("IR compilation failed".to_string()),
Err(e) => Err(format!("IR compilation error: {:?}", e)),
}
}
fn compile_to_asm(&self, source: &str) -> Result<String, String> {
match compile_to_x86_assembly(source) {
Ok(result) if result.success => result
.text_output
.ok_or_else(|| "No assembly output".to_string()),
Ok(_) => Err("Assembly compilation failed".to_string()),
Err(e) => Err(format!("Assembly compilation error: {:?}", e)),
}
}
fn compile_to_obj(&self, source: &str) -> Result<Vec<u8>, String> {
let tmp = std::env::temp_dir().join(format!("x86_cgtest_{}.o", Self::timestamp_ms()));
match compile_to_x86_object(source, &tmp) {
Ok(result) if result.success => result
.binary_output
.ok_or_else(|| "No object output".to_string()),
Ok(_) => Err("Object compilation failed".to_string()),
Err(e) => Err(format!("Object compilation error: {:?}", e)),
}
}
fn find_executable(name: &str) -> bool {
if let Ok(paths) = std::env::var("PATH") {
for dir in paths.split(':') {
let path = format!("{}/{}", dir, name);
if std::fs::metadata(&path)
.map(|m| m.is_file())
.unwrap_or(false)
{
return true;
}
}
}
false
}
fn timestamp_ms() -> u64 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|d| d.as_millis() as u64)
.unwrap_or(0)
}
pub fn reset(&mut self) {
self.results.clear();
self.total_checks = 0;
self.failed_checks = 0;
}
pub fn print_summary(&self) -> String {
format!(
"Verification Summary: {}/{} passed, {} failed",
self.total_checks - self.failed_checks,
self.total_checks,
self.failed_checks
)
}
pub fn all_passed(&self) -> bool {
self.failed_checks == 0
}
}
impl Default for X86CodeGenVerifier {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct X86RngState {
state: u64,
}
impl X86RngState {
pub fn new(seed: Option<u64>) -> Self {
Self {
state: seed.unwrap_or_else(|| {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|d| d.as_nanos() as u64)
.unwrap_or(0xDEAD_BEEF)
}),
}
}
fn next_u64(&mut self) -> u64 {
self.state = self.state.wrapping_add(0x9E3779B97F4A7C15);
let mut z = self.state;
z = (z ^ (z >> 30)).wrapping_mul(0xBF58476D1CE4E5B9);
z = (z ^ (z >> 27)).wrapping_mul(0x94D049BB133111EB);
z ^ (z >> 31)
}
fn next_u32(&mut self) -> u32 {
(self.next_u64() & 0xFFFF_FFFF) as u32
}
fn range(&mut self, min: usize, max: usize) -> usize {
if min >= max {
return min;
}
min + (self.next_u64() as usize % (max - min + 1))
}
fn pick<'a, T>(&mut self, items: &'a [T]) -> Option<&'a T> {
if items.is_empty() {
return None;
}
Some(&items[self.range(0, items.len() - 1)])
}
fn shuffle<T>(&mut self, items: &mut [T]) {
for i in (1..items.len()).rev() {
let j = self.range(0, i);
items.swap(i, j);
}
}
}
pub struct X86TestGenerator {
pub rng: X86RngState,
pub config: X86TestGeneratorConfig,
pub grammar: CProgramGrammar,
pub tests_generated: usize,
}
#[derive(Debug, Clone)]
pub struct X86TestGeneratorConfig {
pub max_expr_depth: usize,
pub max_statements: usize,
pub max_functions: usize,
pub include_structs: bool,
pub include_unions: bool,
pub include_pointers: bool,
pub include_floats: bool,
pub include_inline_asm: bool,
pub include_builtins: bool,
pub include_control_flow: bool,
pub target_features: Vec<String>,
pub generate_valid: bool,
}
impl Default for X86TestGeneratorConfig {
fn default() -> Self {
Self {
max_expr_depth: 5,
max_statements: 20,
max_functions: 5,
include_structs: true,
include_unions: false,
include_pointers: true,
include_floats: true,
include_inline_asm: false,
include_builtins: true,
include_control_flow: true,
target_features: vec![],
generate_valid: true,
}
}
}
#[derive(Debug, Clone)]
pub struct CProgramGrammar {
pub int_types: Vec<String>,
pub float_types: Vec<String>,
pub binary_ops: Vec<String>,
pub unary_ops: Vec<String>,
pub control_flow: Vec<String>,
pub function_names: Vec<String>,
pub var_names: Vec<String>,
pub struct_templates: Vec<String>,
pub builtin_templates: Vec<String>,
}
impl Default for CProgramGrammar {
fn default() -> Self {
Self {
int_types: vec![
"char".into(),
"short".into(),
"int".into(),
"long".into(),
"long long".into(),
"unsigned char".into(),
"unsigned short".into(),
"unsigned int".into(),
"unsigned long".into(),
"unsigned long long".into(),
"int8_t".into(),
"int16_t".into(),
"int32_t".into(),
"int64_t".into(),
"uint8_t".into(),
"uint16_t".into(),
"uint32_t".into(),
"uint64_t".into(),
"intptr_t".into(),
"uintptr_t".into(),
"size_t".into(),
"ssize_t".into(),
"ptrdiff_t".into(),
],
float_types: vec!["float".into(), "double".into(), "long double".into()],
binary_ops: vec![
"+".into(),
"-".into(),
"*".into(),
"/".into(),
"%".into(),
"<<".into(),
">>".into(),
"&".into(),
"|".into(),
"^".into(),
"&&".into(),
"||".into(),
"==".into(),
"!=".into(),
"<".into(),
">".into(),
"<=".into(),
">=".into(),
],
unary_ops: vec!["-".into(), "!".into(), "~".into(), "++".into(), "--".into()],
control_flow: vec![
"if".into(),
"else".into(),
"for".into(),
"while".into(),
"do".into(),
"switch".into(),
"case".into(),
"default".into(),
"break".into(),
"continue".into(),
"goto".into(),
"return".into(),
],
function_names: vec![
"f1".into(),
"f2".into(),
"helper".into(),
"compute".into(),
"process".into(),
"transform".into(),
"validate".into(),
"init".into(),
"cleanup".into(),
"callback".into(),
"handler".into(),
"wrapper".into(),
"entry".into(),
],
var_names: vec![
"x".into(),
"y".into(),
"z".into(),
"a".into(),
"b".into(),
"c".into(),
"d".into(),
"i".into(),
"j".into(),
"k".into(),
"n".into(),
"m".into(),
"tmp".into(),
"result".into(),
"idx".into(),
"pos".into(),
"val".into(),
"ptr".into(),
"buf".into(),
"len".into(),
"cnt".into(),
"acc".into(),
],
struct_templates: vec![
"struct Point { int x; int y; };".into(),
"struct Rect { int x; int y; int w; int h; };".into(),
"struct Vec3 { float x; float y; float z; };".into(),
"struct Pair { int first; int second; };".into(),
"struct Node { int data; struct Node* next; };".into(),
],
builtin_templates: vec![
"__builtin_popcount(i)".into(),
"__builtin_clz(i)".into(),
"__builtin_ctz(i)".into(),
"__builtin_bswap32(i)".into(),
"__builtin_bswap64(i)".into(),
],
}
}
}
impl X86TestGenerator {
pub fn new(seed: Option<u64>) -> Self {
Self {
rng: X86RngState::new(seed),
config: X86TestGeneratorConfig::default(),
grammar: CProgramGrammar::default(),
tests_generated: 0,
}
}
pub fn with_config(mut self, config: X86TestGeneratorConfig) -> Self {
self.config = config;
self
}
pub fn generate_test(&mut self, category: X86TestCategory) -> X86CodeGenTest {
let source = self.generate_c_program();
let name = format!("gen_{}_{}", category, self.tests_generated);
self.tests_generated += 1;
let mut test = X86CodeGenTest::new(&name, category, &source);
test.flags = vec!["-O2".to_string()];
if !self.config.target_features.is_empty() {
let feat_str = self.config.target_features.join(",");
test.flags.push(format!("-march=native"));
test.required_features = self.config.target_features.clone();
}
test
}
pub fn generate_c_program(&mut self) -> String {
let mut buf = String::new();
buf.push_str("#include <stdint.h>\n");
buf.push_str("#include <stddef.h>\n\n");
if self.config.include_structs && self.rng.range(0, 1) == 1 {
for tmpl in &self.grammar.struct_templates {
if self.rng.range(0, 2) == 0 {
buf.push_str(tmpl);
buf.push('\n');
}
}
buf.push('\n');
}
let num_funcs = self.rng.range(1, self.config.max_functions);
let mut func_names: Vec<String> = Vec::new();
for _ in 0..num_funcs {
let ret_type = self.gen_type();
let func_name = self.gen_identifier();
func_names.push(func_name.clone());
let num_params = self.rng.range(0, 4);
let mut params = Vec::new();
for _ in 0..num_params {
let param_type = self.gen_type();
let param_name = self.gen_identifier();
params.push(format!("{} {}", param_type, param_name));
}
buf.push_str(&format!("{} {}(", ret_type, func_name));
buf.push_str(¶ms.join(", "));
buf.push_str(") {\n");
if let Some(name) = std::rc::Rc::new(func_name.clone())
.get(..)
.map(|s| s.to_string())
{
let body = self.gen_function_body(Self::infer_return_type(&ret_type));
buf.push_str(&body);
}
buf.push_str("}\n\n");
}
if !func_names.is_empty() {
buf.push_str(&self.gen_main_with_calls(&func_names));
} else {
buf.push_str("int main(void) { return 0; }\n");
}
buf
}
pub fn generate_edge_case(&mut self, edge_type: &str) -> X86CodeGenTest {
let source = match edge_type {
"int_min" => self.gen_int_min_edge(),
"int_max" => self.gen_int_max_edge(),
"int_wrap" => self.gen_int_wrap_edge(),
"div_zero" => self.gen_div_zero_edge(),
"ptr_null" => self.gen_ptr_null_edge(),
"large_array" => self.gen_large_array_edge(),
"deep_recursion" => self.gen_deep_recursion_edge(),
"many_locals" => self.gen_many_locals_edge(),
"volatile" => self.gen_volatile_edge(),
"const_fold" => self.gen_const_fold_edge(),
_ => self.generate_c_program(),
};
let name = format!("edge_{}_{}", edge_type, self.tests_generated);
self.tests_generated += 1;
X86CodeGenTest::new(&name, X86TestCategory::StressTest, &source)
.with_tags(&["edge-case", edge_type])
}
pub fn reduce_test(&mut self, original: &str, oracle: fn(&str) -> bool) -> String {
let mut current = original.to_string();
let mut changed = true;
while changed {
changed = false;
let lines: Vec<&str> = current.lines().collect();
for i in 0..lines.len() {
let mut candidate: Vec<&str> = lines.clone();
candidate.remove(i);
let candidate_str = candidate.join("\n");
if oracle(&candidate_str) {
current = candidate_str;
changed = true;
break;
}
}
if !changed {
let chars: Vec<char> = current.chars().collect();
for i in 0..chars.len().min(200) {
let mut candidate = chars.clone();
candidate.remove(i);
let candidate_str: String = candidate.iter().collect();
if oracle(&candidate_str) {
current = candidate_str;
changed = true;
break;
}
}
}
}
current
}
pub fn generate_fuzz_input(&mut self) -> String {
let mut buf = String::new();
let length = self.rng.range(50, 2000);
for _ in 0..length {
let choice = self.rng.range(0, 30);
match choice {
0..=10 => buf.push(self.gen_random_alpha()),
11..=15 => buf.push(self.gen_random_digit()),
16..=18 => buf.push(' '),
19 => buf.push('\n'),
20 => buf.push(';'),
21 => buf.push('{'),
22 => buf.push('}'),
23 => buf.push('('),
24 => buf.push(')'),
25..=27 => {
let op = self
.rng
.pick(&["+", "-", "*", "/", "=", "==", "!=", "<", ">", "&", "|"])
.unwrap_or(&"+");
buf.push_str(op);
}
28 => buf.push_str("int "),
29 => buf.push_str("return "),
_ => {}
}
}
buf
}
fn gen_type(&mut self) -> String {
let choice = self.rng.range(0, 3);
match choice {
0 => self
.rng
.pick(&self.grammar.int_types)
.cloned()
.unwrap_or_else(|| "int".to_string()),
1 if self.config.include_floats => self
.rng
.pick(&self.grammar.float_types)
.cloned()
.unwrap_or_else(|| "double".to_string()),
_ => self
.rng
.pick(&self.grammar.int_types)
.cloned()
.unwrap_or_else(|| "int".to_string()),
}
}
fn gen_identifier(&mut self) -> String {
let name = self
.rng
.pick(&self.grammar.var_names)
.cloned()
.unwrap_or_else(|| "v".to_string());
format!("{}_{}", name, self.rng.next_u32() % 1000)
}
fn gen_random_alpha(&self) -> char {
((b'a' + (self.rng.state % 26) as u8) as char)
}
fn gen_random_digit(&self) -> char {
((b'0' + (self.rng.state % 10) as u8) as char)
}
fn gen_function_body(&mut self, ret_type: &str) -> String {
let mut buf = String::new();
let num_stmts = self.rng.range(1, self.config.max_statements);
let num_locals = self.rng.range(0, 5);
for _ in 0..num_locals {
let ty = self.gen_type();
let name = self.gen_identifier();
let init = match self.rng.range(0, 2) {
0 => format!(" = {}", self.rng.next_u32() as i32),
_ => String::new(),
};
buf.push_str(&format!(" {} {}{};\n", ty, name, init));
}
for _ in 0..num_stmts {
if self.config.include_control_flow && self.rng.range(0, 3) == 0 {
buf.push_str(&self.gen_control_flow());
} else {
buf.push_str(" ");
buf.push_str(&self.gen_expression());
buf.push_str(";\n");
}
}
if ret_type != "void" {
let ret_val = match ret_type {
"int" | "long" | "short" | "char" => {
format!("{}", self.rng.next_u32() as i32 % 1000)
}
"float" | "double" => {
format!("{:.1}", (self.rng.next_u32() as f64 % 100.0))
}
_ => "0".to_string(),
};
buf.push_str(&format!(" return {};\n", ret_val));
}
buf
}
fn gen_expression(&mut self) -> String {
match self.rng.range(0, 10) {
0 => format!("{}", self.rng.next_u32() as i32),
1 => self.gen_identifier(),
2..=3 => {
let op = self
.rng
.pick(&self.grammar.binary_ops)
.cloned()
.unwrap_or_else(|| "+".to_string());
format!("{} {} {}", self.gen_expression(), op, self.gen_expression())
}
4 => {
let op = self
.rng
.pick(&self.grammar.unary_ops)
.cloned()
.unwrap_or_else(|| "-".to_string());
format!("{}{}", op, self.gen_expression())
}
_ => format!("{}", self.rng.next_u32() as i32 % 100),
}
}
fn gen_control_flow(&mut self) -> String {
match self.rng.range(0, 5) {
0 => {
format!(
" if ({}) {{\n {};\n }}\n",
self.gen_conditional(),
self.gen_expression()
)
}
1 => {
format!(
" if ({}) {{\n {};\n }} else {{\n {};\n }}\n",
self.gen_conditional(),
self.gen_expression(),
self.gen_expression()
)
}
2 => {
format!(
" for (int {}=0; {0}<{}; {0}++) {{\n {};\n }}\n",
self.gen_identifier(),
self.rng.range(1, 10),
self.gen_expression()
)
}
3 => {
format!(
" while ({}) {{\n {};\n }}\n",
self.gen_conditional(),
self.gen_expression()
)
}
_ => {
format!(
" do {{\n {};\n }} while ({});\n",
self.gen_expression(),
self.gen_conditional()
)
}
}
}
fn gen_conditional(&mut self) -> String {
let op = self
.rng
.pick(&["==", "!=", "<", ">", "<=", ">="])
.unwrap_or(&"<");
format!("{} {} {}", self.gen_expression(), op, self.gen_expression())
}
fn gen_main_with_calls(&mut self, func_names: &[String]) -> String {
let mut buf = String::from("int main(void) {\n");
for name in func_names.iter().take(3) {
buf.push_str(&format!(" {}({});\n", name, self.rng.next_u32() % 100));
}
buf.push_str(" return 0;\n}\n");
buf
}
fn infer_return_type(ret: &str) -> &str {
if ret.contains("void") {
"void"
} else {
"int"
}
}
fn gen_int_min_edge(&self) -> String {
"int main(void) {
int x = -2147483647 - 1; /* INT_MIN */
long y = -9223372036854775807L - 1;
return (x < 0) && (y < 0) ? 0 : 1;
}"
.into()
}
fn gen_int_max_edge(&self) -> String {
"int main(void) {
unsigned int x = 4294967295U;
unsigned long y = 18446744073709551615UL;
return (x > 100000) && (y > 100000) ? 0 : 1;
}"
.into()
}
fn gen_int_wrap_edge(&self) -> String {
"int main(void) {
unsigned char c = 255;
c = c + 1; /* wraps to 0 */
signed char sc = 127;
sc = sc + 1; /* wraps to -128 */
return (c == 0) && (sc == -128) ? 0 : 1;
}"
.into()
}
fn gen_div_zero_edge(&self) -> String {
"int main(void) {
int a = 42, b = 0;
/* Division by zero is UB; just check it compiles */
return 0;
}"
.into()
}
fn gen_ptr_null_edge(&self) -> String {
"int main(void) {
int* p = (int*)0;
int* q = ((void*)0);
return (p == q) ? 0 : 1;
}"
.into()
}
fn gen_large_array_edge(&self) -> String {
"int main(void) {
static char buf[65536];
buf[0] = 'A';
buf[65535] = 'Z';
return (buf[0] == 'A') ? 0 : 1;
}"
.into()
}
fn gen_deep_recursion_edge(&self) -> String {
"int fib(int n) { return n < 2 ? n : fib(n-1) + fib(n-2); }
int main(void) { return fib(10) == 55 ? 0 : 1; }"
.into()
}
fn gen_many_locals_edge(&self) -> String {
let mut buf = String::from("int main(void) {\n");
for i in 0..50 {
buf.push_str(&format!(" int v{} = {};\n", i, i));
}
buf.push_str(" return v49 == 49 ? 0 : 1;\n}\n");
buf
}
fn gen_volatile_edge(&self) -> String {
"int main(void) {
volatile int x = 0;
volatile int y = x + x + x;
return y == 0 ? 0 : 1;
}"
.into()
}
fn gen_const_fold_edge(&self) -> String {
"int main(void) {
int x = 2 + 3 * 4 - 8 / 2 + (7 % 3);
return x == 11 ? 0 : 1;
}"
.into()
}
}
impl Default for X86TestGenerator {
fn default() -> Self {
Self {
rng: X86RngState::new(None),
config: X86TestGeneratorConfig::default(),
grammar: CProgramGrammar::default(),
tests_generated: 0,
}
}
}
#[derive(Debug, Clone)]
pub struct X86BenchmarkMeasurement {
pub name: String,
pub stage_times: HashMap<PipelineStage, Duration>,
pub total_time: Duration,
pub binary_size: usize,
pub ir_size: usize,
pub asm_size: usize,
pub instruction_count: usize,
pub basic_block_count: usize,
pub peak_memory_bytes: Option<u64>,
pub timestamp: SystemTime,
}
impl Default for X86BenchmarkMeasurement {
fn default() -> Self {
Self {
name: String::new(),
stage_times: HashMap::new(),
total_time: Duration::ZERO,
binary_size: 0,
ir_size: 0,
asm_size: 0,
instruction_count: 0,
basic_block_count: 0,
peak_memory_bytes: None,
timestamp: SystemTime::now(),
}
}
}
#[derive(Debug, Clone)]
pub struct X86RegressionThreshold {
pub time_regression_ratio: f64,
pub size_regression_ratio: f64,
pub ir_size_regression_ratio: f64,
pub inst_count_regression_ratio: f64,
}
impl Default for X86RegressionThreshold {
fn default() -> Self {
Self {
time_regression_ratio: 1.10,
size_regression_ratio: 1.05,
ir_size_regression_ratio: 1.10,
inst_count_regression_ratio: 1.10,
}
}
}
#[derive(Debug, Clone)]
pub struct X86RegressionResult {
pub is_regression: bool,
pub description: String,
pub regressed_metric: String,
pub baseline_value: f64,
pub current_value: f64,
pub regression_ratio: f64,
}
pub struct X86CodeGenBenchmark {
pub name: String,
pub options: X86CompileOptions,
pub measurements: Vec<X86BenchmarkMeasurement>,
pub baselines: HashMap<String, X86BenchmarkMeasurement>,
pub thresholds: X86RegressionThreshold,
pub enabled: bool,
pub verbose: bool,
}
impl X86CodeGenBenchmark {
pub fn new(name: &str) -> Self {
Self {
name: name.to_string(),
options: X86CompileOptions::default(),
measurements: Vec::new(),
baselines: HashMap::new(),
thresholds: X86RegressionThreshold::default(),
enabled: true,
verbose: false,
}
}
pub fn with_options(mut self, opts: X86CompileOptions) -> Self {
self.options = opts;
self
}
pub fn verbose(mut self) -> Self {
self.verbose = true;
self
}
pub fn measure(&mut self, name: &str, source: &str) -> X86BenchmarkMeasurement {
let start = Instant::now();
let mut measurement = X86BenchmarkMeasurement {
name: name.to_string(),
timestamp: SystemTime::now(),
..Default::default()
};
let ir_start = Instant::now();
if let Ok(result) = compile_to_x86_ir(source) {
if let Some(ir) = result.text_output {
measurement.ir_size = ir.len();
measurement
.stage_times
.insert(PipelineStage::IRGen, ir_start.elapsed());
}
}
let asm_start = Instant::now();
if let Ok(result) = compile_to_x86_assembly(source) {
if let Some(asm) = result.text_output {
measurement.asm_size = asm.len();
measurement
.stage_times
.insert(PipelineStage::ISel, asm_start.elapsed());
measurement.instruction_count = asm
.lines()
.filter(|l| {
!l.trim().is_empty()
&& !l.trim().starts_with('.')
&& !l.trim().starts_with('#')
&& !l.trim().ends_with(':')
})
.count();
measurement.basic_block_count = asm
.lines()
.filter(|l| l.trim().ends_with(':') && !l.trim().starts_with('.'))
.count();
}
}
let obj_start = Instant::now();
let tmp_obj = std::env::temp_dir().join(format!("x86_bm_{}.o", {
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_nanos())
.unwrap_or(0)
}));
if let Ok(result) = compile_to_x86_object(source, &tmp_obj) {
if let Some(obj) = result.binary_output {
measurement.binary_size = obj.len();
measurement
.stage_times
.insert(PipelineStage::Encode, obj_start.elapsed());
}
let _ = std::fs::remove_file(&tmp_obj);
}
measurement.total_time = start.elapsed();
if self.verbose {
eprintln!(
" Benchmark '{}': {:?} total, {} bytes obj, {} instrs",
name,
measurement.total_time,
measurement.binary_size,
measurement.instruction_count
);
}
self.measurements.push(measurement.clone());
measurement
}
pub fn record_baseline(&mut self, name: &str, measurement: X86BenchmarkMeasurement) {
self.baselines.insert(name.to_string(), measurement);
}
pub fn check_regression(
&self,
name: &str,
current: &X86BenchmarkMeasurement,
) -> Vec<X86RegressionResult> {
let mut regressions = Vec::new();
let baseline = match self.baselines.get(name) {
Some(b) => b,
None => return regressions,
};
let time_ratio =
current.total_time.as_nanos() as f64 / baseline.total_time.as_nanos().max(1) as f64;
if time_ratio > self.thresholds.time_regression_ratio {
regressions.push(X86RegressionResult {
is_regression: true,
description: format!(
"Compilation time regression: {:.2}x slower ({}ms → {}ms)",
time_ratio,
baseline.total_time.as_millis(),
current.total_time.as_millis()
),
regressed_metric: "compilation_time".into(),
baseline_value: baseline.total_time.as_millis() as f64,
current_value: current.total_time.as_millis() as f64,
regression_ratio: time_ratio,
});
}
let size_ratio = current.binary_size as f64 / baseline.binary_size.max(1) as f64;
if size_ratio > self.thresholds.size_regression_ratio {
regressions.push(X86RegressionResult {
is_regression: true,
description: format!(
"Binary size regression: {:.2}x larger ({} → {} bytes)",
size_ratio, baseline.binary_size, current.binary_size
),
regressed_metric: "binary_size".into(),
baseline_value: baseline.binary_size as f64,
current_value: current.binary_size as f64,
regression_ratio: size_ratio,
});
}
let inst_ratio =
current.instruction_count as f64 / baseline.instruction_count.max(1) as f64;
if inst_ratio > self.thresholds.inst_count_regression_ratio {
regressions.push(X86RegressionResult {
is_regression: true,
description: format!(
"Instruction count regression: {:.2}x more ({}, → {})",
inst_ratio, baseline.instruction_count, current.instruction_count
),
regressed_metric: "instruction_count".into(),
baseline_value: baseline.instruction_count as f64,
current_value: current.instruction_count as f64,
regression_ratio: inst_ratio,
});
}
regressions
}
pub fn run_opt_level_benchmark(
&mut self,
source: &str,
) -> HashMap<X86OptLevel, X86BenchmarkMeasurement> {
let levels = [
X86OptLevel::O0,
X86OptLevel::O1,
X86OptLevel::O2,
X86OptLevel::O3,
X86OptLevel::Os,
X86OptLevel::Oz,
];
let mut results = HashMap::new();
for level in &levels {
let mut opts = self.options.clone();
opts.opt_level = *level;
let measurement = self.measure(&format!("opt_{:?}", level), source);
results.insert(*level, measurement);
}
results
}
pub fn average_time(&self) -> Duration {
if self.measurements.is_empty() {
return Duration::ZERO;
}
let total_ns: u128 = self
.measurements
.iter()
.map(|m| m.total_time.as_nanos())
.sum();
Duration::from_nanos((total_ns / self.measurements.len() as u128) as u64)
}
pub fn total_binary_size(&self) -> usize {
self.measurements.iter().map(|m| m.binary_size).sum()
}
pub fn print_report(&self) -> String {
let mut report = String::new();
report.push_str(&format!("Benchmark Report: {}\n", self.name));
report.push_str("========================================\n");
for m in &self.measurements {
report.push_str(&format!(
" {}: {:?} | obj={}B | ir={}B | asm={}B | instrs={}\n",
m.name, m.total_time, m.binary_size, m.ir_size, m.asm_size, m.instruction_count
));
}
report.push_str(&format!("\nAverage time: {:?}\n", self.average_time()));
report.push_str(&format!(
"Total measurements: {}\n",
self.measurements.len()
));
report
}
pub fn reset(&mut self) {
self.measurements.clear();
}
}
impl Default for X86CodeGenBenchmark {
fn default() -> Self {
Self::new("default")
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum OracleComparisonMode {
IRStructure,
AssemblyBehavior,
ExecutionResults,
Full,
}
#[derive(Debug, Clone)]
pub struct OracleComparisonResult {
pub matches: bool,
pub mode: OracleComparisonMode,
pub our_output: String,
pub oracle_output: String,
pub differences: Vec<String>,
pub our_success: bool,
pub oracle_success: bool,
}
pub struct X86Oracle {
pub compiler_path: String,
pub comparison_mode: OracleComparisonMode,
pub oracle_flags: Vec<String>,
pub our_flags: Vec<String>,
pub timeout_secs: u64,
pub results: Vec<OracleComparisonResult>,
}
impl X86Oracle {
pub fn new(compiler_path: &str) -> Self {
Self {
compiler_path: compiler_path.to_string(),
comparison_mode: OracleComparisonMode::Full,
oracle_flags: vec!["-O2".to_string()],
our_flags: vec![],
timeout_secs: 30,
results: Vec::new(),
}
}
pub fn auto_detect() -> Option<Self> {
for cc in &["clang", "gcc", "cc"] {
if let Some(path) = Self::find_compiler(cc) {
return Some(Self::new(&path));
}
}
None
}
pub fn with_mode(mut self, mode: OracleComparisonMode) -> Self {
self.comparison_mode = mode;
self
}
pub fn compare(&mut self, source: &str) -> OracleComparisonResult {
let our_ir = compile_to_x86_ir(source).ok().and_then(|r| r.text_output);
let our_asm = compile_to_x86_assembly(source)
.ok()
.and_then(|r| r.text_output);
let tmp_obj =
std::env::temp_dir().join(format!("x86_oracle_comp_{}.o", Self::timestamp_ns()));
let our_obj = compile_to_x86_object(source, &tmp_obj);
let our_success = our_obj.map(|r| r.success).unwrap_or(false);
let _ = std::fs::remove_file(&tmp_obj);
let oracle_success = self.compile_with_oracle(source, "-S", "-o", "/dev/stdout");
let oracle_obj_success = self.compile_with_oracle(source, "-c", "-o", "/dev/null");
let mut result = OracleComparisonResult {
matches: true,
mode: self.comparison_mode,
our_output: our_asm.clone().unwrap_or_default(),
oracle_output: String::new(),
differences: Vec::new(),
our_success,
oracle_success: oracle_obj_success,
};
if self.comparison_mode == OracleComparisonMode::IRStructure
|| self.comparison_mode == OracleComparisonMode::Full
{
if let Some(ref our) = our_ir {
let oracle_ir =
self.compile_with_oracle_to_string(source, &["-emit-llvm", "-S", "-o", "-"]);
if let Some(oracle) = oracle_ir {
result.oracle_output = oracle.clone();
let diffs = Self::compare_ir_structure(our, &oracle);
if !diffs.is_empty() {
result.matches = false;
result.differences.extend(diffs);
}
}
}
}
if self.comparison_mode == OracleComparisonMode::AssemblyBehavior
|| self.comparison_mode == OracleComparisonMode::Full
{
if let Some(ref our) = our_asm {
let oracle_asm = self.compile_with_oracle_to_string(source, &["-S", "-o", "-"]);
if let Some(oracle) = oracle_asm {
if result.oracle_output.is_empty() {
result.oracle_output = oracle.clone();
}
let diffs = Self::compare_assembly_behavior(our, &oracle);
if !diffs.is_empty() {
result.matches = false;
result.differences.extend(diffs);
}
}
}
}
if self.comparison_mode == OracleComparisonMode::ExecutionResults
|| self.comparison_mode == OracleComparisonMode::Full
{
if our_success && oracle_obj_success {
let our_exe = self.compile_and_run_our(source);
let oracle_exe = self.compile_and_run_oracle(source);
if our_exe != oracle_exe {
result.matches = false;
result.differences.push(format!(
"Execution mismatch: our={:?}, oracle={:?}",
our_exe, oracle_exe
));
}
}
}
self.results.push(result.clone());
result
}
fn compare_ir_structure(our: &str, oracle: &str) -> Vec<String> {
let mut diffs = Vec::new();
let our_funcs = our.matches("define ").count();
let oracle_funcs = oracle.matches("define ").count();
if our_funcs != oracle_funcs {
diffs.push(format!(
"Function count differs: {} vs {}",
our_funcs, oracle_funcs
));
}
for keyword in &["target triple", "target datalayout", "attributes"] {
let our_has = our.contains(keyword);
let oracle_has = oracle.contains(keyword);
if our_has != oracle_has {
diffs.push(format!(
"IR keyword '{}' present={} vs {}",
keyword, our_has, oracle_has
));
}
}
diffs
}
fn compare_assembly_behavior(our: &str, oracle: &str) -> Vec<String> {
let mut diffs = Vec::new();
let our_insts = our
.lines()
.filter(|l| {
!l.trim().is_empty() && !l.trim().starts_with('.') && !l.trim().ends_with(':')
})
.count();
let oracle_insts = oracle
.lines()
.filter(|l| {
!l.trim().is_empty() && !l.trim().starts_with('.') && !l.trim().ends_with(':')
})
.count();
if our_insts == 0 && oracle_insts > 0 {
diffs.push("Our assembly is empty while oracle has instructions".into());
}
for directive in &[".text", ".data", ".globl", ".section"] {
let our_has = our.contains(directive);
let oracle_has = oracle.contains(directive);
if our_has != oracle_has {
diffs.push(format!(
"Directive '{}' present={} vs {}",
directive, our_has, oracle_has
));
}
}
diffs
}
fn compile_with_oracle(
&self,
source: &str,
args: &str,
output_arg: &str,
output_val: &str,
) -> bool {
let temp_dir = match std::env::temp_dir().to_str() {
Some(d) => format!("{}/x86_oracle_{}.c", d, Self::timestamp_ns()),
None => return false,
};
if std::fs::write(&temp_dir, source).is_err() {
return false;
}
let mut cmd = Command::new(&self.compiler_path);
cmd.arg("-x").arg("c");
cmd.arg(&temp_dir);
cmd.arg(args);
cmd.arg(output_arg);
cmd.arg(output_val);
cmd.arg("-w");
for flag in &self.oracle_flags {
cmd.arg(flag);
}
let result = cmd.stdout(Stdio::null()).stderr(Stdio::null()).status();
let _ = std::fs::remove_file(&temp_dir);
result.map(|s| s.success()).unwrap_or(false)
}
fn compile_with_oracle_to_string(&self, source: &str, args: &[&str]) -> Option<String> {
let temp_dir = match std::env::temp_dir().to_str() {
Some(d) => format!("{}/x86_oracle_{}.c", d, Self::timestamp_ns()),
None => return None,
};
if std::fs::write(&temp_dir, source).is_err() {
return None;
}
let mut cmd = Command::new(&self.compiler_path);
cmd.arg("-x").arg("c");
cmd.arg(&temp_dir);
for arg in args {
cmd.arg(arg);
}
cmd.arg("-w");
for flag in &self.oracle_flags {
cmd.arg(flag);
}
let output = cmd.output().ok()?;
let _ = std::fs::remove_file(&temp_dir);
if output.status.success() {
Some(String::from_utf8_lossy(&output.stdout).to_string())
} else {
None
}
}
fn compile_and_run_our(&self, _source: &str) -> Option<i32> {
None
}
fn compile_and_run_oracle(&self, _source: &str) -> Option<i32> {
None
}
fn find_compiler(name: &str) -> Option<String> {
Command::new("which").arg(name).output().ok().and_then(|o| {
if o.status.success() {
let path = String::from_utf8_lossy(&o.stdout).trim().to_string();
if std::fs::metadata(&path).is_ok() {
Some(path)
} else {
None
}
} else {
None
}
})
}
fn timestamp_ns() -> u64 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|d| d.as_nanos() as u64)
.unwrap_or(0)
}
pub fn summary(&self) -> String {
let total = self.results.len();
let matched = self.results.iter().filter(|r| r.matches).count();
format!(
"Oracle Summary: {}/{} match, {} mismatches",
matched,
total,
total - matched
)
}
pub fn reset(&mut self) {
self.results.clear();
}
}
pub fn build_integer_ops_suite() -> X86CodeGenTestSuite {
let mut suite = X86CodeGenTestSuite::new(
"x86-integer-ops",
"Tests for integer arithmetic operations across all widths",
);
let int_widths = [
("char", "signed char"),
("short", "signed short"),
("int", "signed int"),
("long", "signed long"),
("long long", "signed long long"),
("unsigned char", "unsigned char"),
("unsigned short", "unsigned short"),
("unsigned int", "unsigned int"),
("unsigned long", "unsigned long"),
("unsigned long long", "unsigned long long"),
];
for (ty, ty_name) in &int_widths {
suite.add(
X86CodeGenTest::new(
&format!("int_add_{}", ty_name.replace(' ', "_")),
X86TestCategory::IntegerOps,
&format!("{} add_test({} a, {} b) {{ return a + b; }}", ty, ty, ty),
)
.with_tags(&["add", ty_name])
.with_opt_level(X86OptLevel::O2),
);
}
for (ty, ty_name) in &int_widths {
suite.add(
X86CodeGenTest::new(
&format!("int_sub_{}", ty_name.replace(' ', "_")),
X86TestCategory::IntegerOps,
&format!("{} sub_test({} a, {} b) {{ return a - b; }}", ty, ty, ty),
)
.with_tags(&["sub", ty_name]),
);
}
for (ty, ty_name) in &int_widths {
suite.add(
X86CodeGenTest::new(
&format!("int_mul_{}", ty_name.replace(' ', "_")),
X86TestCategory::IntegerOps,
&format!("{} mul_test({} a, {} b) {{ return a * b; }}", ty, ty, ty),
)
.with_tags(&["mul", ty_name]),
);
}
for (ty, ty_name) in &int_widths {
suite.add(
X86CodeGenTest::new(
&format!("int_div_{}", ty_name.replace(' ', "_")),
X86TestCategory::IntegerOps,
&format!("{} div_test({} a, {} b) {{ return a / b; }}", ty, ty, ty),
)
.with_tags(&["div", ty_name]),
);
}
for (ty, ty_name) in &int_widths {
suite.add(
X86CodeGenTest::new(
&format!("int_mod_{}", ty_name.replace(' ', "_")),
X86TestCategory::IntegerOps,
&format!("{} mod_test({} a, {} b) {{ return a %% b; }}", ty, ty, ty),
)
.with_tags(&["mod", ty_name]),
);
}
for (ty, ty_name) in &int_widths {
suite.add(
X86CodeGenTest::new(
&format!("int_and_{}", ty_name.replace(' ', "_")),
X86TestCategory::IntegerOps,
&format!("{} and_test({} a, {} b) {{ return a & b; }}", ty, ty, ty),
)
.with_tags(&["and", ty_name]),
);
}
for (ty, ty_name) in &int_widths {
suite.add(
X86CodeGenTest::new(
&format!("int_or_{}", ty_name.replace(' ', "_")),
X86TestCategory::IntegerOps,
&format!("{} or_test({} a, {} b) {{ return a | b; }}", ty, ty, ty),
)
.with_tags(&["or", ty_name]),
);
}
for (ty, ty_name) in &int_widths {
suite.add(
X86CodeGenTest::new(
&format!("int_xor_{}", ty_name.replace(' ', "_")),
X86TestCategory::IntegerOps,
&format!("{} xor_test({} a, {} b) {{ return a ^ b; }}", ty, ty, ty),
)
.with_tags(&["xor", ty_name]),
);
}
for (ty, ty_name) in &int_widths {
suite.add(
X86CodeGenTest::new(
&format!("int_shl_{}", ty_name.replace(' ', "_")),
X86TestCategory::IntegerOps,
&format!("{} shl_test({} a, int b) {{ return a << b; }}", ty, ty),
)
.with_tags(&["shl", ty_name]),
);
}
for (ty, ty_name) in &int_widths {
suite.add(
X86CodeGenTest::new(
&format!("int_shr_{}", ty_name.replace(' ', "_")),
X86TestCategory::IntegerOps,
&format!("{} shr_test({} a, int b) {{ return a >> b; }}", ty, ty),
)
.with_tags(&["shr", ty_name]),
);
}
for (ty, ty_name) in &int_widths {
suite.add(
X86CodeGenTest::new(
&format!("int_neg_{}", ty_name.replace(' ', "_")),
X86TestCategory::IntegerOps,
&format!("{} neg_test({} a) {{ return -a; }}", ty, ty),
)
.with_tags(&["neg", ty_name]),
);
}
for (ty, ty_name) in &int_widths {
suite.add(
X86CodeGenTest::new(
&format!("int_not_{}", ty_name.replace(' ', "_")),
X86TestCategory::IntegerOps,
&format!("{} not_test({} a) {{ return ~a; }}", ty, ty),
)
.with_tags(&["not", ty_name]),
);
}
suite.add(
X86CodeGenTest::new(
"int_compound_expr",
X86TestCategory::IntegerOps,
"int compound(int a, int b, int c) { return (a + b) * c - (a / (b + 1)); }",
)
.with_tags(&["compound"]),
);
suite.add(X86CodeGenTest::new(
"int_add_overflow",
X86TestCategory::IntegerOps,
"int overflow_add(int a, int b) { int r; return __builtin_add_overflow(a, b, &r) ? -1 : r; }"
).with_tags(&["overflow", "builtin"]));
suite.add(X86CodeGenTest::new(
"int_mul_overflow",
X86TestCategory::IntegerOps,
"int overflow_mul(int a, int b) { int r; return __builtin_mul_overflow(a, b, &r) ? -1 : r; }"
).with_tags(&["overflow", "builtin"]));
suite.add(X86CodeGenTest::new(
"int_sub_overflow",
X86TestCategory::IntegerOps,
"int overflow_sub(int a, int b) { int r; return __builtin_sub_overflow(a, b, &r) ? -1 : r; }"
).with_tags(&["overflow", "builtin"]));
suite.add(
X86CodeGenTest::new(
"int_saturated_add",
X86TestCategory::IntegerOps,
"int sat_add(int a, int b) {
int r;
if (__builtin_add_overflow(a, b, &r))
return a > 0 ? 2147483647 : -2147483648;
return r;
}",
)
.with_tags(&["saturated"]),
);
suite.add(
X86CodeGenTest::new(
"int_mixed_width",
X86TestCategory::IntegerOps,
"long mixed(int a, long b, short c) { return (long)a + b * (long)c; }",
)
.with_tags(&["mixed-width"]),
);
suite
}
pub fn build_floating_point_suite() -> X86CodeGenTestSuite {
let mut suite =
X86CodeGenTestSuite::new("x86-floating-point", "Tests for floating-point operations");
let fp_types = [("float", "f32"), ("double", "f64"), ("long double", "f80")];
for (ty, tag) in &fp_types {
suite.add(
X86CodeGenTest::new(
&format!("fp_add_{}", tag),
X86TestCategory::FloatingPoint,
&format!("{} fp_add({} a, {} b) {{ return a + b; }}", ty, ty, ty),
)
.with_tags(&["fadd", tag]),
);
suite.add(
X86CodeGenTest::new(
&format!("fp_sub_{}", tag),
X86TestCategory::FloatingPoint,
&format!("{} fp_sub({} a, {} b) {{ return a - b; }}", ty, ty, ty),
)
.with_tags(&["fsub", tag]),
);
suite.add(
X86CodeGenTest::new(
&format!("fp_mul_{}", tag),
X86TestCategory::FloatingPoint,
&format!("{} fp_mul({} a, {} b) {{ return a * b; }}", ty, ty, ty),
)
.with_tags(&["fmul", tag]),
);
suite.add(
X86CodeGenTest::new(
&format!("fp_div_{}", tag),
X86TestCategory::FloatingPoint,
&format!("{} fp_div({} a, {} b) {{ return a / b; }}", ty, ty, ty),
)
.with_tags(&["fdiv", tag]),
);
suite.add(
X86CodeGenTest::new(
&format!("fp_neg_{}", tag),
X86TestCategory::FloatingPoint,
&format!("{} fp_neg({} a) {{ return -a; }}", ty, ty),
)
.with_tags(&["fneg", tag]),
);
suite.add(
X86CodeGenTest::new(
&format!("fp_cmp_eq_{}", tag),
X86TestCategory::FloatingPoint,
&format!("int fp_eq({} a, {} b) {{ return a == b; }}", ty, ty),
)
.with_tags(&["fcmp", "eq", tag]),
);
suite.add(
X86CodeGenTest::new(
&format!("fp_cmp_ne_{}", tag),
X86TestCategory::FloatingPoint,
&format!("int fp_ne({} a, {} b) {{ return a != b; }}", ty, ty),
)
.with_tags(&["fcmp", "ne", tag]),
);
suite.add(
X86CodeGenTest::new(
&format!("fp_cmp_lt_{}", tag),
X86TestCategory::FloatingPoint,
&format!("int fp_lt({} a, {} b) {{ return a < b; }}", ty, ty),
)
.with_tags(&["fcmp", "lt", tag]),
);
suite.add(
X86CodeGenTest::new(
&format!("fp_cmp_le_{}", tag),
X86TestCategory::FloatingPoint,
&format!("int fp_le({} a, {} b) {{ return a <= b; }}", ty, ty),
)
.with_tags(&["fcmp", "le", tag]),
);
suite.add(
X86CodeGenTest::new(
&format!("fp_cmp_gt_{}", tag),
X86TestCategory::FloatingPoint,
&format!("int fp_gt({} a, {} b) {{ return a > b; }}", ty, ty),
)
.with_tags(&["fcmp", "gt", tag]),
);
suite.add(
X86CodeGenTest::new(
&format!("fp_cmp_ge_{}", tag),
X86TestCategory::FloatingPoint,
&format!("int fp_ge({} a, {} b) {{ return a >= b; }}", ty, ty),
)
.with_tags(&["fcmp", "ge", tag]),
);
}
suite.add(
X86CodeGenTest::new(
"fp_f2i",
X86TestCategory::FloatingPoint,
"int f2i(float f) { return (int)f; }",
)
.with_tags(&["fptosi"]),
);
suite.add(
X86CodeGenTest::new(
"fp_d2i",
X86TestCategory::FloatingPoint,
"int d2i(double d) { return (int)d; }",
)
.with_tags(&["fptosi"]),
);
suite.add(
X86CodeGenTest::new(
"fp_i2f",
X86TestCategory::FloatingPoint,
"float i2f(int i) { return (float)i; }",
)
.with_tags(&["sitofp"]),
);
suite.add(
X86CodeGenTest::new(
"fp_i2d",
X86TestCategory::FloatingPoint,
"double i2d(int i) { return (double)i; }",
)
.with_tags(&["sitofp"]),
);
suite.add(
X86CodeGenTest::new(
"fp_fpext",
X86TestCategory::FloatingPoint,
"double fpext(float f) { return (double)f; }",
)
.with_tags(&["fpext"]),
);
suite.add(
X86CodeGenTest::new(
"fp_fptrunc",
X86TestCategory::FloatingPoint,
"float fptrunc(double d) { return (float)d; }",
)
.with_tags(&["fptrunc"]),
);
suite.add(
X86CodeGenTest::new(
"fp_nan_constant",
X86TestCategory::FloatingPoint,
"double nan_test(void) { return __builtin_nan(\"\"); }",
)
.with_tags(&["nan", "builtin"]),
);
suite.add(
X86CodeGenTest::new(
"fp_inf_constant",
X86TestCategory::FloatingPoint,
"double inf_test(void) { return __builtin_inf(); }",
)
.with_tags(&["inf", "builtin"]),
);
suite.add(
X86CodeGenTest::new(
"fp_huge_val",
X86TestCategory::FloatingPoint,
"double huge_test(void) { return __builtin_huge_val(); }",
)
.with_tags(&["huge_val", "builtin"]),
);
suite.add(
X86CodeGenTest::new(
"fp_fabs",
X86TestCategory::FloatingPoint,
"double fabs_test(double x) { return __builtin_fabs(x); }",
)
.with_tags(&["fabs", "builtin"]),
);
suite.add(
X86CodeGenTest::new(
"fp_sqrt",
X86TestCategory::FloatingPoint,
"double sqrt_test(double x) { return __builtin_sqrt(x); }",
)
.with_tags(&["sqrt", "builtin"]),
);
suite.add(
X86CodeGenTest::new(
"fp_copysign",
X86TestCategory::FloatingPoint,
"double copysign_test(double x, double y) { return __builtin_copysign(x, y); }",
)
.with_tags(&["copysign", "builtin"]),
);
suite
}
pub fn build_pointer_suite() -> X86CodeGenTestSuite {
let mut suite = X86CodeGenTestSuite::new(
"x86-pointers",
"Tests for pointer operations and pointer arithmetic",
);
suite.add(
X86CodeGenTest::new(
"ptr_load_int",
X86TestCategory::Pointers,
"int load_int(int* p) { return *p; }",
)
.with_tags(&["load"]),
);
suite.add(
X86CodeGenTest::new(
"ptr_store_int",
X86TestCategory::Pointers,
"void store_int(int* p, int v) { *p = v; }",
)
.with_tags(&["store"]),
);
suite.add(
X86CodeGenTest::new(
"ptr_addr_of",
X86TestCategory::Pointers,
"int* addr_of(int* p, int* a) { return &a[0]; }",
)
.with_tags(&["address-of"]),
);
suite.add(
X86CodeGenTest::new(
"ptr_arith_add",
X86TestCategory::Pointers,
"int* ptr_add(int* p, int n) { return p + n; }",
)
.with_tags(&["gep", "add"]),
);
suite.add(
X86CodeGenTest::new(
"ptr_arith_sub",
X86TestCategory::Pointers,
"int* ptr_sub(int* p, int n) { return p - n; }",
)
.with_tags(&["gep", "sub"]),
);
suite.add(
X86CodeGenTest::new(
"ptr_diff",
X86TestCategory::Pointers,
"long ptr_diff(int* a, int* b) { return a - b; }",
)
.with_tags(&["ptrdiff"]),
);
suite.add(
X86CodeGenTest::new(
"ptr_increment",
X86TestCategory::Pointers,
"void ptr_inc(int* p) { p++; *p = 42; }",
)
.with_tags(&["increment"]),
);
suite.add(
X86CodeGenTest::new(
"ptr_decrement",
X86TestCategory::Pointers,
"void ptr_dec(int* p) { p--; *p = 0; }",
)
.with_tags(&["decrement"]),
);
suite.add(
X86CodeGenTest::new(
"ptr_double_indirect",
X86TestCategory::Pointers,
"int ptr_double(int** pp) { return **pp; }",
)
.with_tags(&["double-pointer"]),
);
suite.add(
X86CodeGenTest::new(
"ptr_triple_indirect",
X86TestCategory::Pointers,
"int ptr_triple(int*** ppp) { return ***ppp; }",
)
.with_tags(&["triple-pointer"]),
);
suite.add(
X86CodeGenTest::new(
"ptr_void_ptr",
X86TestCategory::Pointers,
"void* void_ptr_test(int* p) { return (void*)p; }",
)
.with_tags(&["void-pointer"]),
);
suite.add(
X86CodeGenTest::new(
"ptr_void_cast_back",
X86TestCategory::Pointers,
"int* void_cast(void* vp) { return (int*)vp; }",
)
.with_tags(&["void-cast"]),
);
suite.add(
X86CodeGenTest::new(
"ptr_func_ptr_decl",
X86TestCategory::Pointers,
"typedef int (*func_t)(int, int);
int call_func(func_t f, int a, int b) { return f(a, b); }",
)
.with_tags(&["function-pointer"]),
);
suite.add(
X86CodeGenTest::new(
"ptr_func_ptr_array",
X86TestCategory::Pointers,
"int call_via_table(int (*table[])(int), int idx, int arg) {
return table[idx](arg);
}",
)
.with_tags(&["function-pointer", "array"]),
);
suite.add(
X86CodeGenTest::new(
"ptr_null",
X86TestCategory::Pointers,
"int is_null(int* p) { return p == ((void*)0); }",
)
.with_tags(&["null"]),
);
suite.add(
X86CodeGenTest::new(
"ptr_nullptr",
X86TestCategory::Pointers,
"int is_nullptr(int* p) { return p == 0; }",
)
.with_tags(&["null", "constant"]),
);
suite.add(
X86CodeGenTest::new(
"ptr_const_data",
X86TestCategory::Pointers,
"int read_const(const int* p) { return *p; }",
)
.with_tags(&["const"]),
);
suite.add(
X86CodeGenTest::new(
"ptr_const_pointer",
X86TestCategory::Pointers,
"int read_fixed(int* const p) { return *p; }",
)
.with_tags(&["const"]),
);
suite.add(
X86CodeGenTest::new(
"ptr_restrict",
X86TestCategory::Pointers,
"void copy_restrict(int* restrict dst, const int* restrict src, int n) {
for (int i = 0; i < n; i++) dst[i] = src[i];
}",
)
.with_tags(&["restrict"]),
);
suite.add(
X86CodeGenTest::new(
"ptr_volatile",
X86TestCategory::Pointers,
"int read_volatile(volatile int* p) { return *p; }",
)
.with_tags(&["volatile"]),
);
suite.add(
X86CodeGenTest::new(
"ptr_cmp_eq",
X86TestCategory::Pointers,
"int ptr_eq(int* a, int* b) { return a == b; }",
)
.with_tags(&["compare"]),
);
suite.add(
X86CodeGenTest::new(
"ptr_cmp_lt",
X86TestCategory::Pointers,
"int ptr_lt(int* a, int* b) { return a < b; }",
)
.with_tags(&["compare"]),
);
suite.add(
X86CodeGenTest::new(
"ptr_to_struct_field",
X86TestCategory::Pointers,
"struct S { int a; int b; };
int field_access(struct S* s) { return s->a + s->b; }",
)
.with_tags(&["struct", "field"]),
);
suite.add(
X86CodeGenTest::new(
"ptr_alias_same",
X86TestCategory::Pointers,
"void alias(int* a, int* b) { *a = 1; *b = 2; }",
)
.with_tags(&["aliasing"]),
);
suite.add(
X86CodeGenTest::new(
"ptr_alias_struct",
X86TestCategory::Pointers,
"struct S { int x; };
void alias_field(struct S* a, int* b) { a->x = 1; *b = 2; }",
)
.with_tags(&["aliasing", "struct"]),
);
suite
}
pub fn build_aggregates_suite() -> X86CodeGenTestSuite {
let mut suite = X86CodeGenTestSuite::new(
"x86-aggregates",
"Tests for struct, array, and union access",
);
suite.add(
X86CodeGenTest::new(
"struct_simple_load",
X86TestCategory::Aggregates,
"struct Point { int x; int y; };
int get_x(struct Point p) { return p.x; }",
)
.with_tags(&["struct", "load"]),
);
suite.add(
X86CodeGenTest::new(
"struct_simple_store",
X86TestCategory::Aggregates,
"struct Point { int x; int y; };
void set_x(struct Point* p, int v) { p->x = v; }",
)
.with_tags(&["struct", "store"]),
);
suite.add(
X86CodeGenTest::new(
"struct_pass_by_value",
X86TestCategory::Aggregates,
"struct Large { long a; long b; long c; long d; };
long sum(struct Large s) { return s.a + s.b + s.c + s.d; }",
)
.with_tags(&["struct", "pass-by-value"]),
);
suite.add(
X86CodeGenTest::new(
"struct_return_by_value",
X86TestCategory::Aggregates,
"struct Small { int a; int b; };
struct Small make_small(int a, int b) {
struct Small s = { a, b };
return s;
}",
)
.with_tags(&["struct", "return-by-value"]),
);
suite.add(
X86CodeGenTest::new(
"struct_nested",
X86TestCategory::Aggregates,
"struct Inner { int x; };
struct Outer { struct Inner in; int y; };
int get_inner(struct Outer* o) { return o->in.x; }",
)
.with_tags(&["struct", "nested"]),
);
suite.add(
X86CodeGenTest::new(
"struct_self_referential",
X86TestCategory::Aggregates,
"struct Node { int data; struct Node* next; };
int get_data(struct Node* n) { return n->data; }",
)
.with_tags(&["struct", "self-referential"]),
);
suite.add(
X86CodeGenTest::new(
"struct_packed",
X86TestCategory::Aggregates,
"struct __attribute__((packed)) Packed { char a; int b; short c; };
int get_b_packed(struct Packed* p) { return p->b; }",
)
.with_tags(&["struct", "packed"]),
);
suite.add(
X86CodeGenTest::new(
"struct_bitfield",
X86TestCategory::Aggregates,
"struct BF { int a:1; int b:7; int c:8; int d:16; };
int get_bits(struct BF* bf) { return bf->a + bf->b + bf->c + bf->d; }",
)
.with_tags(&["struct", "bitfield"]),
);
suite.add(
X86CodeGenTest::new(
"struct_anonymous",
X86TestCategory::Aggregates,
"struct Outer { int a; struct { int x; int y; }; int b; };
int get_inner(struct Outer* o) { return o->x + o->y; }",
)
.with_tags(&["struct", "anonymous"]),
);
suite.add(
X86CodeGenTest::new(
"struct_array_of_structs",
X86TestCategory::Aggregates,
"struct S { int a; int b; };
int sum_array(struct S arr[], int n) {
int s = 0;
for (int i = 0; i < n; i++) s += arr[i].a + arr[i].b;
return s;
}",
)
.with_tags(&["struct", "array"]),
);
suite.add(
X86CodeGenTest::new(
"array_static_access",
X86TestCategory::Aggregates,
"int access(int arr[], int i) { return arr[i]; }",
)
.with_tags(&["array", "index"]),
);
suite.add(
X86CodeGenTest::new(
"array_static_write",
X86TestCategory::Aggregates,
"void write(int arr[], int i, int v) { arr[i] = v; }",
)
.with_tags(&["array", "write"]),
);
suite.add(
X86CodeGenTest::new(
"array_static_init",
X86TestCategory::Aggregates,
"int get_static(int i) {
static int arr[] = {1, 2, 3, 4, 5};
return arr[i];
}",
)
.with_tags(&["array", "static-init"]),
);
suite.add(
X86CodeGenTest::new(
"array_multi_dim",
X86TestCategory::Aggregates,
"int matrix(int a[3][4], int i, int j) { return a[i][j]; }",
)
.with_tags(&["array", "multi-dim"]),
);
suite.add(
X86CodeGenTest::new(
"array_large_index",
X86TestCategory::Aggregates,
"int large_idx(char* buf, int i) { return buf[i * 4096 + 2048]; }",
)
.with_tags(&["array", "large-index"]),
);
suite.add(
X86CodeGenTest::new(
"array_string_literal",
X86TestCategory::Aggregates,
"char first(const char* s) { return s[0]; }",
)
.with_tags(&["array", "string"]),
);
suite.add(
X86CodeGenTest::new(
"array_zero_length",
X86TestCategory::Aggregates,
"struct Flex { int len; char data[0]; };
int flex_size(void) { return sizeof(struct Flex); }",
)
.with_tags(&["array", "flexible"]),
);
suite.add(
X86CodeGenTest::new(
"array_vla_simple",
X86TestCategory::Aggregates,
"int vla_sum(int n) {
int arr[n];
for (int i = 0; i < n; i++) arr[i] = i;
return arr[n-1];
}",
)
.with_tags(&["array", "vla"]),
);
suite.add(
X86CodeGenTest::new(
"union_simple",
X86TestCategory::Aggregates,
"union U { int i; float f; };
float as_float(int i) { union U u; u.i = i; return u.f; }",
)
.with_tags(&["union", "type-punning"]),
);
suite.add(
X86CodeGenTest::new(
"union_pointer",
X86TestCategory::Aggregates,
"union U { int i; float f; };
int get_int(union U* u) { return u->i; }",
)
.with_tags(&["union", "pointer"]),
);
suite.add(
X86CodeGenTest::new(
"union_struct",
X86TestCategory::Aggregates,
"struct S { int tag; union { int i; float f; } data; };
int get_int(struct S* s) { return s->data.i; }",
)
.with_tags(&["union", "struct-member"]),
);
suite.add(
X86CodeGenTest::new(
"sizeof_basic",
X86TestCategory::Aggregates,
"int sizes(void) {
return sizeof(char) + sizeof(short) + sizeof(int) + sizeof(long);
}",
)
.with_tags(&["sizeof"]),
);
suite.add(
X86CodeGenTest::new(
"sizeof_struct",
X86TestCategory::Aggregates,
"struct Mixed { char a; int b; char c; };
int struct_size(void) { return sizeof(struct Mixed); }",
)
.with_tags(&["sizeof", "struct"]),
);
suite.add(
X86CodeGenTest::new(
"alignof_basic",
X86TestCategory::Aggregates,
"int aligns(void) {
return __alignof__(int) + __alignof__(double) + __alignof__(char);
}",
)
.with_tags(&["alignof"]),
);
suite
}
pub fn build_control_flow_suite() -> X86CodeGenTestSuite {
let mut suite =
X86CodeGenTestSuite::new("x86-control-flow", "Tests for control flow structures");
suite.add(
X86CodeGenTest::new(
"cf_if_simple",
X86TestCategory::ControlFlow,
"int simple_if(int x) { if (x) return 1; return 0; }",
)
.with_tags(&["if"]),
);
suite.add(
X86CodeGenTest::new(
"cf_if_else",
X86TestCategory::ControlFlow,
"int if_else(int x) { if (x > 0) return 1; else return -1; }",
)
.with_tags(&["if", "else"]),
);
suite.add(
X86CodeGenTest::new(
"cf_if_chain",
X86TestCategory::ControlFlow,
"int if_chain(int x) {
if (x < 0) return -1;
else if (x == 0) return 0;
else return 1;
}",
)
.with_tags(&["if", "chain"]),
);
suite.add(
X86CodeGenTest::new(
"cf_nested_if",
X86TestCategory::ControlFlow,
"int nested_if(int a, int b) {
if (a) { if (b) return 1; return 2; }
return 3;
}",
)
.with_tags(&["if", "nested"]),
);
suite.add(
X86CodeGenTest::new(
"cf_ternary",
X86TestCategory::ControlFlow,
"int ternary(int x, int y) { return x > y ? x : y; }",
)
.with_tags(&["ternary"]),
);
suite.add(
X86CodeGenTest::new(
"cf_for_simple",
X86TestCategory::ControlFlow,
"int for_sum(int n) {
int s = 0;
for (int i = 0; i < n; i++) s += i;
return s;
}",
)
.with_tags(&["for"]),
);
suite.add(
X86CodeGenTest::new(
"cf_for_empty",
X86TestCategory::ControlFlow,
"void for_empty(int n) { for (int i = 0; i < n; i++) ; }",
)
.with_tags(&["for", "empty"]),
);
suite.add(
X86CodeGenTest::new(
"cf_for_break",
X86TestCategory::ControlFlow,
"int for_break(int n) {
int s = 0;
for (int i = 0; i < n; i++) { if (i > 10) break; s += i; }
return s;
}",
)
.with_tags(&["for", "break"]),
);
suite.add(
X86CodeGenTest::new(
"cf_for_continue",
X86TestCategory::ControlFlow,
"int for_continue(int n) {
int s = 0;
for (int i = 0; i < n; i++) { if (i % 2 == 0) continue; s += i; }
return s;
}",
)
.with_tags(&["for", "continue"]),
);
suite.add(
X86CodeGenTest::new(
"cf_for_nested",
X86TestCategory::ControlFlow,
"int nested_for(int n, int m) {
int s = 0;
for (int i = 0; i < n; i++)
for (int j = 0; j < m; j++)
s += i * j;
return s;
}",
)
.with_tags(&["for", "nested"]),
);
suite.add(
X86CodeGenTest::new(
"cf_for_no_init",
X86TestCategory::ControlFlow,
"int for_no_init(int n) {
int i = 0, s = 0;
for (; i < n; i++) s += i;
return s;
}",
)
.with_tags(&["for", "no-init"]),
);
suite.add(
X86CodeGenTest::new(
"cf_while_simple",
X86TestCategory::ControlFlow,
"int while_sum(int n) {
int i = 0, s = 0;
while (i < n) { s += i; i++; }
return s;
}",
)
.with_tags(&["while"]),
);
suite.add(
X86CodeGenTest::new(
"cf_while_break",
X86TestCategory::ControlFlow,
"int while_break(int n) {
int i = 0;
while (1) { if (i >= n) break; i++; }
return i;
}",
)
.with_tags(&["while", "break"]),
);
suite.add(
X86CodeGenTest::new(
"cf_while_continue",
X86TestCategory::ControlFlow,
"int while_continue(int n) {
int i = 0, s = 0;
while (i < n) { i++; if (i % 2) continue; s += i; }
return s;
}",
)
.with_tags(&["while", "continue"]),
);
suite.add(
X86CodeGenTest::new(
"cf_do_while_simple",
X86TestCategory::ControlFlow,
"int do_while_count(int n) {
int i = 0;
do { i++; } while (i < n);
return i;
}",
)
.with_tags(&["do-while"]),
);
suite.add(
X86CodeGenTest::new(
"cf_do_while_once",
X86TestCategory::ControlFlow,
"int do_while_once(void) {
int x = 0;
do { x = 1; } while (0);
return x;
}",
)
.with_tags(&["do-while", "once"]),
);
suite.add(
X86CodeGenTest::new(
"cf_switch_simple",
X86TestCategory::ControlFlow,
"int switch_simple(int x) {
switch (x) {
case 0: return 10;
case 1: return 20;
case 2: return 30;
default: return 0;
}
}",
)
.with_tags(&["switch"]),
);
suite.add(
X86CodeGenTest::new(
"cf_switch_fallthrough",
X86TestCategory::ControlFlow,
"int switch_fall(int x) {
int r = 0;
switch (x) {
case 0: r += 1;
case 1: r += 2; break;
case 2: r += 4; break;
}
return r;
}",
)
.with_tags(&["switch", "fallthrough"]),
);
suite.add(
X86CodeGenTest::new(
"cf_switch_sparse",
X86TestCategory::ControlFlow,
"int switch_sparse(int x) {
switch (x) {
case 1: return 1;
case 100: return 100;
case 1000: return 1000;
default: return -1;
}
}",
)
.with_tags(&["switch", "sparse"]),
);
suite.add(
X86CodeGenTest::new(
"cf_switch_many_cases",
X86TestCategory::ControlFlow,
"int switch_many(int x) {
switch (x) {
case 0: return 0; case 1: return 1; case 2: return 2;
case 3: return 3; case 4: return 4; case 5: return 5;
case 6: return 6; case 7: return 7; case 8: return 8;
case 9: return 9; case 10: return 10; case 11: return 11;
case 12: return 12; case 13: return 13; case 14: return 14;
case 15: return 15;
default: return -1;
}
}",
)
.with_tags(&["switch", "many-cases"]),
);
suite.add(
X86CodeGenTest::new(
"cf_goto_simple",
X86TestCategory::ControlFlow,
"int goto_simple(int x) {
if (x > 0) goto positive;
return -1;
positive:
return 1;
}",
)
.with_tags(&["goto"]),
);
suite.add(
X86CodeGenTest::new(
"cf_goto_loop",
X86TestCategory::ControlFlow,
"int goto_loop(int n) {
int i = 0, s = 0;
loop:
if (i >= n) goto done;
s += i; i++;
goto loop;
done:
return s;
}",
)
.with_tags(&["goto", "loop"]),
);
suite.add(
X86CodeGenTest::new(
"cf_computed_goto",
X86TestCategory::ControlFlow,
"int computed_goto(int idx) {
static void* table[] = { &&L0, &&L1, &&L2 };
if (idx < 0 || idx > 2) return -1;
goto *table[idx];
L0: return 0;
L1: return 1;
L2: return 2;
}",
)
.with_tags(&["goto", "computed"]),
);
suite.add(
X86CodeGenTest::new(
"cf_label_value",
X86TestCategory::ControlFlow,
"void* get_label(void) { return &&label; label: return &&label; }",
)
.with_tags(&["label", "value"]),
);
suite
}
pub fn build_functions_suite() -> X86CodeGenTestSuite {
let mut suite = X86CodeGenTestSuite::new(
"x86-functions",
"Tests for function declarations, calls, and attributes",
);
suite.add(
X86CodeGenTest::new(
"func_no_params",
X86TestCategory::Functions,
"int no_params() { return 42; }",
)
.with_tags(&["no-params"]),
);
suite.add(
X86CodeGenTest::new(
"func_void_params",
X86TestCategory::Functions,
"int void_params(void) { return 0; }",
)
.with_tags(&["void-params"]),
);
suite.add(
X86CodeGenTest::new(
"func_many_params",
X86TestCategory::Functions,
"int many(int a, int b, int c, int d, int e, int f, int g, int h) {
return a + b + c + d + e + f + g + h;
}",
)
.with_tags(&["many-params"]),
);
suite.add(
X86CodeGenTest::new(
"func_void_return",
X86TestCategory::Functions,
"void do_nothing(void) { }",
)
.with_tags(&["void"]),
);
suite.add(
X86CodeGenTest::new(
"func_struct_return",
X86TestCategory::Functions,
"struct Big { long a,b,c,d; };
struct Big make_big(long x) {
struct Big b = {x,x,x,x};
return b;
}",
)
.with_tags(&["struct-return"]),
);
suite.add(
X86CodeGenTest::new(
"func_recursive_factorial",
X86TestCategory::Functions,
"int factorial(int n) { return n <= 1 ? 1 : n * factorial(n - 1); }",
)
.with_tags(&["recursion", "factorial"]),
);
suite.add(
X86CodeGenTest::new(
"func_recursive_fibonacci",
X86TestCategory::Functions,
"int fib(int n) { return n < 2 ? n : fib(n-1) + fib(n-2); }",
)
.with_tags(&["recursion", "fibonacci"]),
);
suite.add(
X86CodeGenTest::new(
"func_mutual_recursion",
X86TestCategory::Functions,
"int even(int n);
int odd(int n) { return n == 0 ? 0 : even(n - 1); }
int even(int n) { return n == 0 ? 1 : odd(n - 1); }",
)
.with_tags(&["recursion", "mutual"]),
);
suite.add(
X86CodeGenTest::new(
"func_tail_recursion",
X86TestCategory::Functions,
"int tail_fact(int n, int acc) {
return n <= 1 ? acc : tail_fact(n - 1, n * acc);
}",
)
.with_tags(&["recursion", "tail"]),
);
suite.add(
X86CodeGenTest::new(
"func_inline_hint",
X86TestCategory::Functions,
"inline int add_inline(int a, int b) { return a + b; }",
)
.with_tags(&["inline"]),
);
suite.add(
X86CodeGenTest::new(
"func_always_inline",
X86TestCategory::Functions,
"__attribute__((always_inline)) int add_always(int a, int b) { return a + b; }",
)
.with_tags(&["always_inline"]),
);
suite.add(
X86CodeGenTest::new(
"func_noinline",
X86TestCategory::Functions,
"__attribute__((noinline)) int noinline_func(int x) { return x * x; }",
)
.with_tags(&["noinline"]),
);
suite.add(
X86CodeGenTest::new(
"func_noreturn",
X86TestCategory::Functions,
"__attribute__((noreturn)) void die(void) { while(1); }",
)
.with_tags(&["noreturn"]),
);
suite.add(
X86CodeGenTest::new(
"func_pure",
X86TestCategory::Functions,
"__attribute__((pure)) int square(int x) { return x * x; }",
)
.with_tags(&["pure"]),
);
suite.add(
X86CodeGenTest::new(
"func_const",
X86TestCategory::Functions,
"__attribute__((const)) int compute(int x) { return x * 42; }",
)
.with_tags(&["const"]),
);
suite.add(
X86CodeGenTest::new(
"func_varargs_decl",
X86TestCategory::Functions,
"#include <stdarg.h>
int sum_varargs(int count, ...) {
va_list args;
va_start(args, count);
int s = 0;
for (int i = 0; i < count; i++) s += va_arg(args, int);
va_end(args);
return s;
}",
)
.with_tags(&["varargs"]),
);
suite.add(
X86CodeGenTest::new(
"func_varargs_printf_style",
X86TestCategory::Functions,
"#include <stdarg.h>
int my_printf(const char* fmt, ...) {
va_list args;
va_start(args, fmt);
int count = 0;
while (*fmt) {
if (*fmt == '%') { va_arg(args, int); count++; }
fmt++;
}
va_end(args);
return count;
}",
)
.with_tags(&["varargs", "printf"]),
);
suite.add(
X86CodeGenTest::new(
"func_call_chain",
X86TestCategory::Functions,
"int a(int x) { return x + 1; }
int b(int x) { return a(x) * 2; }
int c(int x) { return b(x) + 3; }",
)
.with_tags(&["call-chain"]),
);
suite.add(
X86CodeGenTest::new(
"func_indirect_call",
X86TestCategory::Functions,
"int call_func(int (*f)(int), int x) { return f(x); }",
)
.with_tags(&["indirect"]),
);
suite.add(
X86CodeGenTest::new(
"func_knr_style",
X86TestCategory::Functions,
"int old_style(a, b) int a; int b; { return a + b; }",
)
.with_tags(&["knr"]),
);
suite
}
pub fn build_cpp_features_suite() -> X86CodeGenTestSuite {
let mut suite =
X86CodeGenTestSuite::new("x86-cpp-features", "Tests for C++ code generation features");
suite.add(
X86CodeGenTest::new(
"cpp_virtual_simple",
X86TestCategory::CPlusPlusFeatures,
"class Base { public: virtual int f() { return 1; } };
class Derived : public Base { public: int f() override { return 2; } };
int call_virtual(Base* b) { return b->f(); }",
)
.with_tags(&["cpp", "virtual"]),
);
suite.add(
X86CodeGenTest::new(
"cpp_virtual_dtor",
X86TestCategory::CPlusPlusFeatures,
"class Base { public: virtual ~Base() {} };
class Derived : public Base { public: ~Derived() {} };
void delete_base(Base* b) { delete b; }",
)
.with_tags(&["cpp", "virtual", "dtor"]),
);
suite.add(
X86CodeGenTest::new(
"cpp_pure_virtual",
X86TestCategory::CPlusPlusFeatures,
"class Abstract { public: virtual int f() = 0; };
int call_abstract(Abstract* a) { return a->f(); }",
)
.with_tags(&["cpp", "pure-virtual"]),
);
suite.add(
X86CodeGenTest::new(
"cpp_template_func",
X86TestCategory::CPlusPlusFeatures,
"template<typename T>
T add(T a, T b) { return a + b; }
int use_template(int x, int y) { return add(x, y); }",
)
.with_tags(&["cpp", "template"]),
);
suite.add(
X86CodeGenTest::new(
"cpp_template_class",
X86TestCategory::CPlusPlusFeatures,
"template<typename T>
class Pair { T a, b; public: Pair(T x, T y) : a(x), b(y) {} T sum() { return a + b; } };
int use_pair() { Pair<int> p(1, 2); return p.sum(); }",
)
.with_tags(&["cpp", "template", "class"]),
);
suite.add(
X86CodeGenTest::new(
"cpp_template_specialization",
X86TestCategory::CPlusPlusFeatures,
"template<typename T> T identity(T x) { return x; }
template<> int identity(int x) { return x + 1; }
int use_special() { return identity(1); }",
)
.with_tags(&["cpp", "template", "specialization"]),
);
suite.add(
X86CodeGenTest::new(
"cpp_rtti_typeid",
X86TestCategory::CPlusPlusFeatures,
"#include <typeinfo>
const char* type_name(int) { return typeid(int).name(); }",
)
.with_tags(&["cpp", "rtti"]),
);
suite.add(
X86CodeGenTest::new(
"cpp_rtti_dynamic_cast",
X86TestCategory::CPlusPlusFeatures,
"class Base { public: virtual ~Base() {} };
class Derived : public Base {};
Derived* downcast(Base* b) { return dynamic_cast<Derived*>(b); }",
)
.with_tags(&["cpp", "rtti", "dynamic-cast"]),
);
suite.add(
X86CodeGenTest::new(
"cpp_exception_try_catch",
X86TestCategory::CPlusPlusFeatures,
"int try_catch(int x) {
try { if (x < 0) throw 0; return x; }
catch (int) { return -1; }
}",
)
.with_tags(&["cpp", "exception"]),
);
suite.add(
X86CodeGenTest::new(
"cpp_exception_rethrow",
X86TestCategory::CPlusPlusFeatures,
"void rethrow_test() {
try { throw 1; }
catch (...) {
try { throw; }
catch (int) { }
}
}",
)
.with_tags(&["cpp", "exception", "rethrow"]),
);
suite.add(
X86CodeGenTest::new(
"cpp_operator_plus",
X86TestCategory::CPlusPlusFeatures,
"struct Vec { int x, y;
Vec operator+(const Vec& o) const { return {x+o.x, y+o.y}; }
};
Vec add_vec(const Vec& a, const Vec& b) { return a + b; }",
)
.with_tags(&["cpp", "operator"]),
);
suite.add(
X86CodeGenTest::new(
"cpp_ctor_default",
X86TestCategory::CPlusPlusFeatures,
"class Wrapper { int* p; public:
Wrapper(int x) : p(new int(x)) {}
~Wrapper() { delete p; }
int get() const { return *p; }
};
int use_wrapper() { Wrapper w(42); return w.get(); }",
)
.with_tags(&["cpp", "ctor", "dtor"]),
);
suite.add(
X86CodeGenTest::new(
"cpp_namespace",
X86TestCategory::CPlusPlusFeatures,
"namespace N { int f() { return 1; } }
int call_ns() { return N::f(); }",
)
.with_tags(&["cpp", "namespace"]),
);
suite.add(
X86CodeGenTest::new(
"cpp_lambda_simple",
X86TestCategory::CPlusPlusFeatures,
"int use_lambda(int x) {
auto f = [](int a) { return a * 2; };
return f(x);
}",
)
.with_tags(&["cpp", "lambda"]),
);
suite.add(
X86CodeGenTest::new(
"cpp_lambda_capture",
X86TestCategory::CPlusPlusFeatures,
"int use_capture(int y) {
auto f = [y](int x) { return x + y; };
return f(10);
}",
)
.with_tags(&["cpp", "lambda", "capture"]),
);
suite.add(
X86CodeGenTest::new(
"cpp_lambda_mutable",
X86TestCategory::CPlusPlusFeatures,
"int use_mutable() {
int x = 0;
auto f = [x]() mutable { return ++x; };
f(); f();
return f();
}",
)
.with_tags(&["cpp", "lambda", "mutable"]),
);
suite
}
pub fn build_x86_builtins_suite() -> X86CodeGenTestSuite {
let mut suite =
X86CodeGenTestSuite::new("x86-builtins", "Tests for X86-specific compiler builtins");
suite.add(
X86CodeGenTest::new(
"builtin_popcount",
X86TestCategory::X86Builtins,
"int popcount_test(unsigned x) { return __builtin_popcount(x); }",
)
.with_tags(&["popcount"]),
);
suite.add(
X86CodeGenTest::new(
"builtin_popcountl",
X86TestCategory::X86Builtins,
"int popcountl_test(unsigned long x) { return __builtin_popcountl(x); }",
)
.with_tags(&["popcount", "long"]),
);
suite.add(
X86CodeGenTest::new(
"builtin_popcountll",
X86TestCategory::X86Builtins,
"int popcountll_test(unsigned long long x) { return __builtin_popcountll(x); }",
)
.with_tags(&["popcount", "long-long"]),
);
suite.add(
X86CodeGenTest::new(
"builtin_clz",
X86TestCategory::X86Builtins,
"int clz_test(unsigned x) { return __builtin_clz(x); }",
)
.with_tags(&["clz"]),
);
suite.add(
X86CodeGenTest::new(
"builtin_ctz",
X86TestCategory::X86Builtins,
"int ctz_test(unsigned x) { return __builtin_ctz(x); }",
)
.with_tags(&["ctz"]),
);
suite.add(
X86CodeGenTest::new(
"builtin_clrsb",
X86TestCategory::X86Builtins,
"int clrsb_test(int x) { return __builtin_clrsb(x); }",
)
.with_tags(&["clrsb"]),
);
suite.add(
X86CodeGenTest::new(
"builtin_ffs",
X86TestCategory::X86Builtins,
"int ffs_test(int x) { return __builtin_ffs(x); }",
)
.with_tags(&["ffs"]),
);
suite.add(
X86CodeGenTest::new(
"builtin_parity",
X86TestCategory::X86Builtins,
"int parity_test(unsigned x) { return __builtin_parity(x); }",
)
.with_tags(&["parity"]),
);
suite.add(
X86CodeGenTest::new(
"builtin_bswap16",
X86TestCategory::X86Builtins,
"unsigned short bswap16_test(unsigned short x) { return __builtin_bswap16(x); }",
)
.with_tags(&["bswap"]),
);
suite.add(
X86CodeGenTest::new(
"builtin_bswap32",
X86TestCategory::X86Builtins,
"unsigned bswap32_test(unsigned x) { return __builtin_bswap32(x); }",
)
.with_tags(&["bswap"]),
);
suite.add(
X86CodeGenTest::new(
"builtin_bswap64",
X86TestCategory::X86Builtins,
"unsigned long long bswap64_test(unsigned long long x) {
return __builtin_bswap64(x);
}",
)
.with_tags(&["bswap"]),
);
suite.add(
X86CodeGenTest::new(
"builtin_expect",
X86TestCategory::X86Builtins,
"int expect_test(int x) { return __builtin_expect(x, 0); }",
)
.with_tags(&["expect"]),
);
suite.add(
X86CodeGenTest::new(
"builtin_prefetch",
X86TestCategory::X86Builtins,
"void prefetch_test(void* p) { __builtin_prefetch(p); }",
)
.with_tags(&["prefetch"]),
);
suite.add(
X86CodeGenTest::new(
"builtin_trap",
X86TestCategory::X86Builtins,
"void trap_test(void) { __builtin_trap(); }",
)
.with_tags(&["trap"]),
);
suite.add(
X86CodeGenTest::new(
"builtin_unreachable",
X86TestCategory::X86Builtins,
"int unreachable_test(int x) {
if (x < 0) __builtin_unreachable();
return x;
}",
)
.with_tags(&["unreachable"]),
);
suite.add(
X86CodeGenTest::new(
"builtin_types_compatible_p",
X86TestCategory::X86Builtins,
"int compat_test(void) { return __builtin_types_compatible_p(int, long); }",
)
.with_tags(&["types-compatible"]),
);
suite.add(
X86CodeGenTest::new(
"builtin_choose_expr",
X86TestCategory::X86Builtins,
"int choose_test(void) { return __builtin_choose_expr(1, 42, 0); }",
)
.with_tags(&["choose-expr"]),
);
suite.add(
X86CodeGenTest::new(
"builtin_constant_p",
X86TestCategory::X86Builtins,
"int constp_test(int x) { return __builtin_constant_p(x); }",
)
.with_tags(&["constant-p"]),
);
suite.add(
X86CodeGenTest::new(
"builtin_object_size",
X86TestCategory::X86Builtins,
"int objsize_test(void) {
char buf[10];
return __builtin_object_size(buf, 0);
}",
)
.with_tags(&["object-size"]),
);
suite.add(
X86CodeGenTest::new(
"builtin_assume_aligned",
X86TestCategory::X86Builtins,
"void* aligned_test(void* p) { return __builtin_assume_aligned(p, 16); }",
)
.with_tags(&["assume-aligned"]),
);
suite
}
pub fn build_abi_tests_suite() -> X86CodeGenTestSuite {
let mut suite = X86CodeGenTestSuite::new(
"x86-abi",
"Tests for System V AMD64 and Win64 calling conventions",
);
suite.add(
X86CodeGenTest::new(
"abi_sysv_int_params",
X86TestCategory::ABITests,
"int sysv_int(int a, int b, int c, int d, int e, int f, int g, int h) {
return a + b + c + d + e + f + g + h;
}",
)
.with_tags(&["sysv", "int-args"]),
);
suite.add(
X86CodeGenTest::new(
"abi_sysv_float_params",
X86TestCategory::ABITests,
"float sysv_float(float a, float b, float c, float d) {
return a + b + c + d;
}",
)
.with_tags(&["sysv", "float-args"]),
);
suite.add(
X86CodeGenTest::new(
"abi_sysv_mixed_params",
X86TestCategory::ABITests,
"double sysv_mixed(int a, double b, int c, float d, long e, double f) {
return a + b + c + d + e + f;
}",
)
.with_tags(&["sysv", "mixed-args"]),
);
suite.add(
X86CodeGenTest::new(
"abi_sysv_struct_pass",
X86TestCategory::ABITests,
"struct Small { int a; int b; };
int sysv_struct_pass(struct Small s) { return s.a + s.b; }",
)
.with_tags(&["sysv", "struct-pass"]),
);
suite.add(
X86CodeGenTest::new(
"abi_sysv_large_struct_pass",
X86TestCategory::ABITests,
"struct Large { long a,b,c,d,e; };
long sysv_large_pass(struct Large s) { return s.a + s.b + s.c + s.d + s.e; }",
)
.with_tags(&["sysv", "large-struct"]),
);
suite.add(
X86CodeGenTest::new(
"abi_sysv_struct_return",
X86TestCategory::ABITests,
"struct Pair { int a; int b; };
struct Pair sysv_ret_struct(int a, int b) {
struct Pair p = {a, b};
return p;
}",
)
.with_tags(&["sysv", "struct-return"]),
);
suite.add(
X86CodeGenTest::new(
"abi_sysv_red_zone",
X86TestCategory::ABITests,
"int red_zone_test(int x) {
int a = 1;
return x + a;
}",
)
.with_tags(&["sysv", "red-zone"]),
);
suite.add(
X86CodeGenTest::new(
"abi_sysv_varargs",
X86TestCategory::ABITests,
"#include <stdarg.h>
int sysv_va(int count, ...) {
va_list args;
va_start(args, count);
int s = 0;
for (int i = 0; i < count; i++) s += va_arg(args, int);
va_end(args);
return s;
}",
)
.with_tags(&["sysv", "varargs"]),
);
suite.add(
X86CodeGenTest::new(
"abi_sysv_va_list_struct",
X86TestCategory::ABITests,
"#include <stdarg.h>
struct S { int a; };
int va_struct(int count, ...) {
va_list args;
va_start(args, count);
int s = 0;
for (int i = 0; i < count; i++) {
struct S v = va_arg(args, struct S);
s += v.a;
}
va_end(args);
return s;
}",
)
.with_tags(&["sysv", "varargs", "struct"]),
);
suite
}
pub fn build_optimization_suite() -> X86CodeGenTestSuite {
let mut suite = X86CodeGenTestSuite::new(
"x86-optimization",
"Tests for correctness after optimization passes",
);
let opt_levels = [
(X86OptLevel::O0, "O0"),
(X86OptLevel::O1, "O1"),
(X86OptLevel::O2, "O2"),
(X86OptLevel::O3, "O3"),
(X86OptLevel::Os, "Os"),
(X86OptLevel::Oz, "Oz"),
];
for (level, tag) in &opt_levels {
suite.add(
X86CodeGenTest::new(
&format!("opt_{}_constant_fold", tag),
X86TestCategory::OptimizationTests,
"int const_fold(void) { return 2 + 3 * 4 - 8 / 2; }",
)
.with_opt_level(*level)
.with_tags(&["constant-fold", tag]),
);
suite.add(
X86CodeGenTest::new(
&format!("opt_{}_dead_code", tag),
X86TestCategory::OptimizationTests,
"int dead_code(int x) { int y = x * 2; return x; }",
)
.with_opt_level(*level)
.with_tags(&["dead-code", tag]),
);
suite.add(
X86CodeGenTest::new(
&format!("opt_{}_common_subexpr", tag),
X86TestCategory::OptimizationTests,
"int cse(int a, int b) {
int x = a * b + 1;
int y = a * b + 2;
return x + y;
}",
)
.with_opt_level(*level)
.with_tags(&["cse", tag]),
);
suite.add(
X86CodeGenTest::new(
&format!("opt_{}_loop_invariant", tag),
X86TestCategory::OptimizationTests,
"int loop_invar(int n) {
int s = 0;
for (int i = 0; i < n; i++) s += 42;
return s;
}",
)
.with_opt_level(*level)
.with_tags(&["licm", tag]),
);
suite.add(
X86CodeGenTest::new(
&format!("opt_{}_tail_call", tag),
X86TestCategory::OptimizationTests,
"int tail(int n, int acc) {
return n == 0 ? acc : tail(n - 1, acc + n);
}",
)
.with_opt_level(*level)
.with_tags(&["tail-call", tag]),
);
}
suite
}
pub fn build_all_x86_codegen_suites() -> Vec<X86CodeGenTestSuite> {
vec![
build_integer_ops_suite(),
build_floating_point_suite(),
build_pointer_suite(),
build_aggregates_suite(),
build_control_flow_suite(),
build_functions_suite(),
build_cpp_features_suite(),
build_x86_builtins_suite(),
build_abi_tests_suite(),
build_optimization_suite(),
]
}
pub fn build_stress_test_suite() -> X86CodeGenTestSuite {
let mut suite = X86CodeGenTestSuite::new("x86-stress", "Stress tests for X86 code generation");
suite.add(
X86CodeGenTest::new(
"stress_many_locals",
X86TestCategory::StressTest,
&generate_many_locals_source(100),
)
.with_tags(&["many-locals"]),
);
suite.add(
X86CodeGenTest::new(
"stress_deep_nesting",
X86TestCategory::StressTest,
&generate_deep_nesting_source(10),
)
.with_tags(&["deep-nesting"]),
);
suite.add(
X86CodeGenTest::new(
"stress_large_switch",
X86TestCategory::StressTest,
&generate_large_switch_source(50),
)
.with_tags(&["large-switch"]),
);
suite.add(
X86CodeGenTest::new(
"stress_complex_expr",
X86TestCategory::StressTest,
"int complex(int a, int b, int c, int d, int e) {
return (((a * b + c) / (d + 1)) ^ ((e << 2) & 0xFF)) |
(((a - b) * (c + d)) % ((e | 0xF) + 1));
}",
)
.with_tags(&["complex-expr"]),
);
suite.add(
X86CodeGenTest::new(
"stress_call_chain",
X86TestCategory::StressTest,
&generate_call_chain_source(20),
)
.with_tags(&["call-chain"]),
);
suite.add(
X86CodeGenTest::new(
"stress_large_array",
X86TestCategory::StressTest,
"int large_array(void) {
static int arr[1000];
int s = 0;
for (int i = 0; i < 1000; i++) {
arr[i] = i * i;
s += arr[i];
}
return s;
}",
)
.with_tags(&["large-array"]),
);
suite.add(
X86CodeGenTest::new(
"stress_aliasing",
X86TestCategory::StressTest,
"void alias_stress(int* a, int* b, int* c, int* d, int n) {
for (int i = 0; i < n; i++) {
*a = *b + *c;
*d = *a * 2;
a++; b++; c++; d++;
}
}",
)
.with_tags(&["aliasing"]),
);
suite
}
fn generate_many_locals_source(count: usize) -> String {
let mut buf = String::from("int many_locals(void) {\n");
for i in 0..count {
buf.push_str(&format!(" int v{} = {};\n", i, i));
}
buf.push_str(" int s = 0;\n");
for i in 0..count {
buf.push_str(&format!(" s += v{};\n", i));
}
buf.push_str(" return s;\n}\n");
buf
}
fn generate_deep_nesting_source(depth: usize) -> String {
let mut buf = String::from("int deep_nest(int x) {\n");
for i in 0..depth {
buf.push_str(&format!("{}if (x > {}) {{\n", " ".repeat(i + 1), i));
}
buf.push_str(&format!("{}return x;\n", " ".repeat(depth + 1)));
for i in (0..depth).rev() {
buf.push_str(&format!("{}}}\n", " ".repeat(i + 1)));
}
buf.push_str(" return 0;\n}\n");
buf
}
fn generate_large_switch_source(num_cases: usize) -> String {
let mut buf = String::from("int large_switch(int x) {\n switch (x) {\n");
for i in 0..num_cases {
buf.push_str(&format!(" case {}: return {};\n", i, i * 3));
}
buf.push_str(" default: return -1;\n }\n}\n");
buf
}
fn generate_call_chain_source(depth: usize) -> String {
let mut buf = String::new();
for i in 0..depth {
let next = if i + 1 < depth {
format!("f{}(x + 1)", i + 1)
} else {
"x".to_string()
};
buf.push_str(&format!("int f{}(int x) {{ return {}; }}\n", i, next));
}
buf
}
#[derive(Debug, Clone)]
pub struct X86RegressionTestCase {
pub id: String,
pub description: String,
pub source: String,
pub expected_behavior: String,
pub date_added: String,
pub issue_ref: Option<String>,
}
pub struct X86RegressionRegistry {
pub tests: Vec<X86RegressionTestCase>,
}
impl X86RegressionRegistry {
pub fn new() -> Self {
Self { tests: Vec::new() }
}
pub fn register(&mut self, test: X86RegressionTestCase) {
self.tests.push(test);
}
pub fn all_sources(&self) -> Vec<&str> {
self.tests.iter().map(|t| t.source.as_str()).collect()
}
pub fn count(&self) -> usize {
self.tests.len()
}
}
impl Default for X86RegressionRegistry {
fn default() -> Self {
Self::new()
}
}
pub fn build_regression_suite() -> X86RegressionRegistry {
let mut reg = X86RegressionRegistry::new();
reg.register(X86RegressionTestCase {
id: "REG-001".into(),
description: "Zero-length array in struct causes miscompile".into(),
source: "struct Flex { int len; char data[0]; };
int test(void) { return sizeof(struct Flex); }"
.into(),
expected_behavior: "sizeof returns correct value".into(),
date_added: "2025-01-01".into(),
issue_ref: None,
});
reg.register(X86RegressionTestCase {
id: "REG-002".into(),
description: "Bitfield access with negative values".into(),
source: "struct BF { signed int a:3; };
int test(struct BF* b) { return b->a; }"
.into(),
expected_behavior: "Sign extension correct".into(),
date_added: "2025-01-15".into(),
issue_ref: None,
});
reg.register(X86RegressionTestCase {
id: "REG-003".into(),
description: "Large struct return causes stack corruption".into(),
source: "struct Big { long a,b,c,d,e,f,g,h; };
struct Big make(void) {
struct Big b = {1,2,3,4,5,6,7,8};
return b;
}"
.into(),
expected_behavior: "Stack frame correct".into(),
date_added: "2025-02-01".into(),
issue_ref: None,
});
reg.register(X86RegressionTestCase {
id: "REG-004".into(),
description: "Computed goto with empty label table".into(),
source: "int test(void) {
void* table[] = {};
return 0;
}"
.into(),
expected_behavior: "Compiles without crash".into(),
date_added: "2025-02-15".into(),
issue_ref: None,
});
reg.register(X86RegressionTestCase {
id: "REG-005".into(),
description: "SIMD intrinsics with misaligned stack".into(),
source: "#include <x86intrin.h>
__m128i test(__m128i a, __m128i b) {
return _mm_add_epi32(a, b);
}"
.into(),
expected_behavior: "Correct 16-byte alignment".into(),
date_added: "2025-03-01".into(),
issue_ref: None,
});
reg
}
#[derive(Debug)]
pub struct X86TestSuiteResult {
pub suite_name: String,
pub total: usize,
pub passed: usize,
pub failed: usize,
pub skipped: usize,
pub xfailed: usize,
pub xpassed: usize,
pub duration: Duration,
pub failures: Vec<String>,
}
impl X86TestSuiteResult {
pub fn new(name: &str) -> Self {
Self {
suite_name: name.to_string(),
total: 0,
passed: 0,
failed: 0,
skipped: 0,
xfailed: 0,
xpassed: 0,
duration: Duration::ZERO,
failures: Vec::new(),
}
}
pub fn pass_rate(&self) -> f64 {
if self.total == 0 {
100.0
} else {
(self.passed as f64 / self.total as f64) * 100.0
}
}
pub fn is_perfect(&self) -> bool {
self.failed == 0
}
}
pub struct X86CodeGenTestRunner {
pub verifier: X86CodeGenVerifier,
pub benchmark: X86CodeGenBenchmark,
pub oracle: Option<X86Oracle>,
pub suites: Vec<X86CodeGenTestSuite>,
pub results: Vec<X86TestSuiteResult>,
pub stop_on_failure: bool,
pub verbose: bool,
}
impl X86CodeGenTestRunner {
pub fn new() -> Self {
Self {
verifier: X86CodeGenVerifier::new(),
benchmark: X86CodeGenBenchmark::new("x86-codegen"),
oracle: X86Oracle::auto_detect(),
suites: Vec::new(),
results: Vec::new(),
stop_on_failure: false,
verbose: false,
}
}
pub fn verbose(mut self) -> Self {
self.verbose = true;
self.verifier.verbose = true;
self
}
pub fn add_suite(&mut self, suite: X86CodeGenTestSuite) {
self.suites.push(suite);
}
pub fn run_all(&mut self) -> Vec<X86TestSuiteResult> {
let start = Instant::now();
for suite in &self.suites {
let result = self.run_suite(suite);
self.results.push(result);
if self.stop_on_failure && self.results.last().map_or(false, |r| r.failed > 0) {
break;
}
}
let total_time = start.elapsed();
eprintln!("All suites completed in {:?}", total_time);
self.results.clone()
}
fn run_suite(&self, suite: &X86CodeGenTestSuite) -> X86TestSuiteResult {
let start = Instant::now();
let mut result = X86TestSuiteResult::new(&suite.name);
result.total = suite.tests.len();
for test in &suite.tests {
let mut verifier = self.verifier.clone();
let check = verifier.verify(test);
if test.is_xfail {
if check.is_pass() {
result.xpassed += 1;
} else {
result.xfailed += 1;
}
} else if test.expect_compiles() && !check.compiled() {
result.failed += 1;
result
.failures
.push(format!("{}: compilation failed", test.name));
} else if !check.is_pass() {
result.failed += 1;
result
.failures
.push(format!("{}: verification failed", test.name));
} else {
result.passed += 1;
}
}
result.duration = start.elapsed();
if self.verbose {
eprintln!(
"Suite '{}': {}/{} passed ({:.1}%) in {:?}",
suite.name,
result.passed,
result.total,
result.pass_rate(),
result.duration
);
}
result
}
pub fn print_summary(&self) -> String {
let mut report = String::new();
report.push_str("╔══════════════════════════════════════════════════════════════╗\n");
report.push_str("║ X86 Code Generation Test Summary ║\n");
report.push_str("╚══════════════════════════════════════════════════════════════╝\n\n");
let mut total_all = 0;
let mut passed_all = 0;
let mut failed_all = 0;
for result in &self.results {
report.push_str(&format!(
" {}: {}/{} passed, {} failed, {} xfailed, {} xpassed\n",
result.suite_name,
result.passed,
result.total,
result.failed,
result.xfailed,
result.xpassed
));
total_all += result.total;
passed_all += result.passed;
failed_all += result.failed;
}
report.push_str(&format!(
"\n TOTAL: {}/{} passed, {} failed ({:.1}% pass rate)\n",
passed_all,
total_all,
failed_all,
if total_all > 0 {
(passed_all as f64 / total_all as f64) * 100.0
} else {
0.0
}
));
report
}
pub fn run_benchmarks(&mut self) -> String {
let mut report = String::from("X86 CodeGen Benchmark Report\n");
report.push_str("============================\n\n");
for suite in &self.suites {
for test in &suite.tests.iter().take(5) {
let measurement = self.benchmark.measure(&test.name, &test.source);
report.push_str(&format!(
" {}: {:?}, {}b obj, {} instrs\n",
test.name,
measurement.total_time,
measurement.binary_size,
measurement.instruction_count
));
}
}
report.push_str(&format!(
"\nAverage time: {:?}\n",
self.benchmark.average_time()
));
report
}
}
impl Default for X86CodeGenTestRunner {
fn default() -> Self {
Self::new()
}
}
impl fmt::Display for X86CodeGenTestRunner {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.print_summary())
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum FuzzStatus {
Pass,
Crash {
signal: i32,
context: String,
},
Timeout,
CompileError {
message: String,
},
MISCompare {
our_output: String,
oracle_output: String,
},
}
pub struct X86CodeGenFuzzer {
pub generator: X86TestGenerator,
pub verifier: X86CodeGenVerifier,
pub max_iterations: usize,
pub timeout_ms: u64,
pub iterations: usize,
pub unique_crashes: HashSet<String>,
pub results: Vec<(String, FuzzStatus)>,
}
impl X86CodeGenFuzzer {
pub fn new(seed: Option<u64>) -> Self {
Self {
generator: X86TestGenerator::new(seed),
verifier: X86CodeGenVerifier::new(),
max_iterations: 1000,
timeout_ms: 5000,
iterations: 0,
unique_crashes: HashSet::new(),
results: Vec::new(),
}
}
pub fn with_max_iterations(mut self, max: usize) -> Self {
self.max_iterations = max;
self
}
pub fn run(&mut self) -> Vec<(String, FuzzStatus)> {
for _ in 0..self.max_iterations {
self.iterations += 1;
let source = self.generator.generate_fuzz_input();
let start = Instant::now();
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
self.verifier.compile_to_ir(&source)
}));
let status = match result {
Ok(Ok(ir)) => {
if ir.contains("error") || ir.is_empty() {
FuzzStatus::CompileError {
message: "Invalid IR generated".into(),
}
} else {
FuzzStatus::Pass
}
}
Ok(Err(e)) => FuzzStatus::CompileError { message: e },
Err(_) => {
let crash_key = format!("crash_{}", self.iterations);
self.unique_crashes.insert(crash_key.clone());
FuzzStatus::Crash {
signal: -1,
context: "Panic in compilation".into(),
}
}
};
if self.iterations % 100 == 0 {
eprintln!(
"Fuzz iteration {}: {} passes, {} crashes",
self.iterations,
self.results
.iter()
.filter(|(_, s)| *s == FuzzStatus::Pass)
.count(),
self.unique_crashes.len()
);
}
self.results
.push((format!("fuzz_{}", self.iterations), status));
}
self.results.clone()
}
pub fn print_summary(&self) -> String {
let passes = self
.results
.iter()
.filter(|(_, s)| *s == FuzzStatus::Pass)
.count();
let crashes = self
.results
.iter()
.filter(|(_, s)| matches!(s, FuzzStatus::Crash { .. }))
.count();
let errors = self
.results
.iter()
.filter(|(_, s)| matches!(s, FuzzStatus::CompileError { .. }))
.count();
format!(
"Fuzz Summary: {} iterations, {} passes, {} crashes, {} errors, {} unique crashes",
self.iterations,
passes,
crashes,
errors,
self.unique_crashes.len()
)
}
}
impl Default for X86CodeGenFuzzer {
fn default() -> Self {
Self::new(None)
}
}
pub fn discover_tests_in_directory(dir: &Path) -> Vec<X86CodeGenTest> {
let mut tests = Vec::new();
if let Ok(entries) = std::fs::read_dir(dir) {
for entry in entries.flatten() {
let path = entry.path();
if path.extension().map_or(false, |e| e == "c" || e == "cpp") {
if let Ok(source) = std::fs::read_to_string(&path) {
let name = path
.file_stem()
.and_then(|s| s.to_str())
.unwrap_or("unknown");
let test = X86CodeGenTest::new(name, X86TestCategory::Regression, &source);
tests.push(test);
}
}
}
}
tests
}
pub fn build_inline_asm_suite() -> X86CodeGenTestSuite {
let mut suite = X86CodeGenTestSuite::new(
"x86-inline-asm",
"Tests for X86 inline assembly code generation",
);
suite.add(
X86CodeGenTest::new(
"asm_basic_nop",
X86TestCategory::InlineAssembly,
"void asm_nop(void) { __asm__ volatile(\"nop\"); }",
)
.with_tags(&["asm", "nop"]),
);
suite.add(
X86CodeGenTest::new(
"asm_mov_imm",
X86TestCategory::InlineAssembly,
"int asm_mov(void) { int x; __asm__(\"mov $42, %0\" : \"=r\"(x)); return x; }",
)
.with_tags(&["asm", "mov"]),
);
suite.add(
X86CodeGenTest::new(
"asm_add_operands",
X86TestCategory::InlineAssembly,
"int asm_add(int a, int b) {
int result;
__asm__(\"add %1, %2; mov %2, %0\"
: \"=r\"(result)
: \"r\"(a), \"r\"(b));
return result;
}",
)
.with_tags(&["asm", "add", "operands"]),
);
suite.add(
X86CodeGenTest::new(
"asm_memory_clobber",
X86TestCategory::InlineAssembly,
"void asm_clobber(void) {
int x = 0;
__asm__ volatile(\"\" : : : \"memory\");
x = 42;
}",
)
.with_tags(&["asm", "clobber", "memory"]),
);
suite.add(
X86CodeGenTest::new(
"asm_register_clobber",
X86TestCategory::InlineAssembly,
"int asm_reg_clobber(void) {
int x = 1;
__asm__ volatile(\"xor %%eax, %%eax\" : : : \"eax\");
return x;
}",
)
.with_tags(&["asm", "clobber", "register"]),
);
suite.add(
X86CodeGenTest::new(
"asm_input_output_constraint",
X86TestCategory::InlineAssembly,
"int asm_io(int input) {
int output;
__asm__(\"add $1, %0\" : \"=r\"(output) : \"0\"(input));
return output;
}",
)
.with_tags(&["asm", "input-output"]),
);
suite.add(
X86CodeGenTest::new(
"asm_multiple_outputs",
X86TestCategory::InlineAssembly,
"void asm_multi_out(int a, int b, int* sum, int* diff) {
__asm__(\"add %3, %2; sub %3, %1\"
: \"=r\"(*sum), \"=r\"(*diff)
: \"r\"(a), \"r\"(b));
}",
)
.with_tags(&["asm", "multiple-outputs"]),
);
suite.add(
X86CodeGenTest::new(
"asm_goto_label",
X86TestCategory::InlineAssembly,
"int asm_goto_test(int x) {
__asm__ goto(\"test %0, %0; jne %l[label]\"
: : \"r\"(x) : : label);
return 0;
label:
return 1;
}",
)
.with_tags(&["asm", "goto"]),
);
suite.add(
X86CodeGenTest::new(
"asm_cpuid",
X86TestCategory::InlineAssembly,
"void asm_cpuid(int code, unsigned int* a, unsigned int* b,
unsigned int* c, unsigned int* d) {
__asm__ volatile(\"cpuid\"
: \"=a\"(*a), \"=b\"(*b), \"=c\"(*c), \"=d\"(*d)
: \"a\"(code));
}",
)
.with_tags(&["asm", "cpuid"]),
);
suite.add(
X86CodeGenTest::new(
"asm_rdtsc",
X86TestCategory::InlineAssembly,
"unsigned long long asm_rdtsc(void) {
unsigned int lo, hi;
__asm__ volatile(\"rdtsc\" : \"=a\"(lo), \"=d\"(hi));
return ((unsigned long long)hi << 32) | lo;
}",
)
.with_tags(&["asm", "rdtsc"]),
);
suite.add(
X86CodeGenTest::new(
"asm_xgetbv",
X86TestCategory::InlineAssembly,
"unsigned long long asm_xgetbv(unsigned int ecx) {
unsigned int eax, edx;
__asm__ volatile(\"xgetbv\" : \"=a\"(eax), \"=d\"(edx) : \"c\"(ecx));
return ((unsigned long long)edx << 32) | eax;
}",
)
.with_tags(&["asm", "xgetbv"]),
);
suite.add(
X86CodeGenTest::new(
"asm_rdrand",
X86TestCategory::InlineAssembly,
"int asm_rdrand(unsigned int* val) {
unsigned char ok;
__asm__ volatile(\"rdrand %0; setc %1\"
: \"=r\"(*val), \"=qm\"(ok));
return (int)ok;
}",
)
.with_tags(&["asm", "rdrand"]),
);
suite.add(
X86CodeGenTest::new(
"asm_pause",
X86TestCategory::InlineAssembly,
"void asm_pause(void) { __asm__ volatile(\"pause\"); }",
)
.with_tags(&["asm", "pause"]),
);
suite.add(
X86CodeGenTest::new(
"asm_mfence",
X86TestCategory::InlineAssembly,
"void asm_mfence(void) { __asm__ volatile(\"mfence\" : : : \"memory\"); }",
)
.with_tags(&["asm", "mfence"]),
);
suite.add(
X86CodeGenTest::new(
"asm_sfence",
X86TestCategory::InlineAssembly,
"void asm_sfence(void) { __asm__ volatile(\"sfence\" : : : \"memory\"); }",
)
.with_tags(&["asm", "sfence"]),
);
suite.add(
X86CodeGenTest::new(
"asm_lfence",
X86TestCategory::InlineAssembly,
"void asm_lfence(void) { __asm__ volatile(\"lfence\" : : : \"memory\"); }",
)
.with_tags(&["asm", "lfence"]),
);
suite.add(
X86CodeGenTest::new(
"asm_xchg",
X86TestCategory::InlineAssembly,
"int asm_xchg(int* ptr, int val) {
__asm__ volatile(\"xchg %0, %1\" : \"+r\"(val), \"+m\"(*ptr));
return val;
}",
)
.with_tags(&["asm", "xchg", "atomic"]),
);
suite.add(
X86CodeGenTest::new(
"asm_cmpxchg",
X86TestCategory::InlineAssembly,
"int asm_cmpxchg(int* ptr, int old, int new) {
int prev;
__asm__ volatile(\"lock; cmpxchg %2, %1\"
: \"=a\"(prev), \"+m\"(*ptr)
: \"r\"(new), \"0\"(old));
return prev;
}",
)
.with_tags(&["asm", "cmpxchg", "atomic"]),
);
suite.add(
X86CodeGenTest::new(
"asm_bsr",
X86TestCategory::InlineAssembly,
"int asm_bsr(unsigned int x) {
int r;
__asm__(\"bsr %1, %0\" : \"=r\"(r) : \"r\"(x));
return r;
}",
)
.with_tags(&["asm", "bsr"]),
);
suite.add(
X86CodeGenTest::new(
"asm_bsf",
X86TestCategory::InlineAssembly,
"int asm_bsf(unsigned int x) {
int r;
__asm__(\"bsf %1, %0\" : \"=r\"(r) : \"r\"(x));
return r;
}",
)
.with_tags(&["asm", "bsf"]),
);
suite.add(
X86CodeGenTest::new(
"asm_imul_two_operands",
X86TestCategory::InlineAssembly,
"int asm_imul(int a, int b) {
int r;
__asm__(\"imul %1, %2; mov %2, %0\"
: \"=r\"(r) : \"r\"(a), \"r\"(b));
return r;
}",
)
.with_tags(&["asm", "imul"]),
);
suite.add(
X86CodeGenTest::new(
"asm_idiv",
X86TestCategory::InlineAssembly,
"int asm_idiv(int dividend, int divisor, int* remainder) {
int quot;
__asm__(\"cdq; idiv %3\"
: \"=a\"(quot), \"=d\"(*remainder)
: \"a\"(dividend), \"r\"(divisor));
return quot;
}",
)
.with_tags(&["asm", "idiv"]),
);
suite
}
pub fn build_vector_ops_suite() -> X86CodeGenTestSuite {
let mut suite = X86CodeGenTestSuite::new(
"x86-vector-ops",
"Tests for SIMD vector operations code generation",
);
suite.add(
X86CodeGenTest::new(
"vec_typedef_extension",
X86TestCategory::VectorOps,
"typedef int v4si __attribute__((vector_size(16)));
int sum_v4si(v4si v) { return v[0] + v[1] + v[2] + v[3]; }",
)
.with_tags(&["vector", "typedef"]),
);
suite.add(
X86CodeGenTest::new(
"vec_add_v4sf",
X86TestCategory::VectorOps,
"typedef float v4sf __attribute__((vector_size(16)));
v4sf add_v4sf(v4sf a, v4sf b) { return a + b; }",
)
.with_tags(&["vector", "add", "float"]),
);
suite.add(
X86CodeGenTest::new(
"vec_sub_v4sf",
X86TestCategory::VectorOps,
"typedef float v4sf __attribute__((vector_size(16)));
v4sf sub_v4sf(v4sf a, v4sf b) { return a - b; }",
)
.with_tags(&["vector", "sub"]),
);
suite.add(
X86CodeGenTest::new(
"vec_mul_v4sf",
X86TestCategory::VectorOps,
"typedef float v4sf __attribute__((vector_size(16)));
v4sf mul_v4sf(v4sf a, v4sf b) { return a * b; }",
)
.with_tags(&["vector", "mul"]),
);
suite.add(
X86CodeGenTest::new(
"vec_div_v4sf",
X86TestCategory::VectorOps,
"typedef float v4sf __attribute__((vector_size(16)));
v4sf div_v4sf(v4sf a, v4sf b) { return a / b; }",
)
.with_tags(&["vector", "div"]),
);
suite.add(
X86CodeGenTest::new(
"vec_v2di_ops",
X86TestCategory::VectorOps,
"typedef long long v2di __attribute__((vector_size(16)));
v2di add_v2di(v2di a, v2di b) { return a + b; }",
)
.with_tags(&["vector", "v2di"]),
);
suite.add(
X86CodeGenTest::new(
"vec_v8hi_ops",
X86TestCategory::VectorOps,
"typedef short v8hi __attribute__((vector_size(16)));
v8hi add_v8hi(v8hi a, v8hi b) { return a + b; }",
)
.with_tags(&["vector", "v8hi"]),
);
suite.add(
X86CodeGenTest::new(
"vec_v16qi_ops",
X86TestCategory::VectorOps,
"typedef char v16qi __attribute__((vector_size(16)));
v16qi add_v16qi(v16qi a, v16qi b) { return a + b; }",
)
.with_tags(&["vector", "v16qi"]),
);
suite.add(
X86CodeGenTest::new(
"vec_shuffle",
X86TestCategory::VectorOps,
"typedef int v4si __attribute__((vector_size(16)));
v4si shuffle_v4si(v4si a, v4si b) {
return __builtin_shufflevector(a, b, 0, 4, 1, 5);
}",
)
.with_tags(&["vector", "shuffle"]),
);
suite.add(
X86CodeGenTest::new(
"vec_extract_element",
X86TestCategory::VectorOps,
"typedef int v4si __attribute__((vector_size(16)));
int extract(v4si v) { return v[2]; }",
)
.with_tags(&["vector", "extract"]),
);
suite.add(
X86CodeGenTest::new(
"vec_insert_element",
X86TestCategory::VectorOps,
"typedef int v4si __attribute__((vector_size(16)));
v4si insert(v4si v, int x) { v[0] = x; return v; }",
)
.with_tags(&["vector", "insert"]),
);
suite.add(
X86CodeGenTest::new(
"vec_compare_eq",
X86TestCategory::VectorOps,
"typedef int v4si __attribute__((vector_size(16)));
v4si cmp_eq(v4si a, v4si b) { return a == b; }",
)
.with_tags(&["vector", "compare", "eq"]),
);
suite.add(
X86CodeGenTest::new(
"vec_compare_lt",
X86TestCategory::VectorOps,
"typedef int v4si __attribute__((vector_size(16)));
v4si cmp_lt(v4si a, v4si b) { return a < b; }",
)
.with_tags(&["vector", "compare", "lt"]),
);
suite.add(
X86CodeGenTest::new(
"vec_v8sf_avx",
X86TestCategory::VectorOps,
"typedef float v8sf __attribute__((vector_size(32)));
v8sf add_v8sf(v8sf a, v8sf b) { return a + b; }",
)
.with_tags(&["vector", "avx", "v8sf"]),
);
suite.add(
X86CodeGenTest::new(
"vec_v4df_avx",
X86TestCategory::VectorOps,
"typedef double v4df __attribute__((vector_size(32)));
v4df add_v4df(v4df a, v4df b) { return a + b; }",
)
.with_tags(&["vector", "avx", "v4df"]),
);
suite.add(
X86CodeGenTest::new(
"vec_reduction_sum",
X86TestCategory::VectorOps,
"typedef int v4si __attribute__((vector_size(16)));
int reduce_sum(v4si v) {
v4si t = __builtin_shufflevector(v, v, 1, 0, 3, 2);
v4si s = v + t;
return s[0] + s[2];
}",
)
.with_tags(&["vector", "reduction"]),
);
suite.add(
X86CodeGenTest::new(
"vec_load_unaligned",
X86TestCategory::VectorOps,
"typedef int v4si __attribute__((vector_size(16)));
v4si load_vec(const int* p) {
return *(const v4si*)p;
}",
)
.with_tags(&["vector", "load"]),
);
suite.add(
X86CodeGenTest::new(
"vec_store_unaligned",
X86TestCategory::VectorOps,
"typedef int v4si __attribute__((vector_size(16)));
void store_vec(int* p, v4si v) {
*(v4si*)p = v;
}",
)
.with_tags(&["vector", "store"]),
);
suite
}
pub fn build_memory_ordering_suite() -> X86CodeGenTestSuite {
let mut suite = X86CodeGenTestSuite::new(
"x86-memory-ordering",
"Tests for atomic operations and memory ordering",
);
suite.add(
X86CodeGenTest::new(
"atomic_load_relaxed",
X86TestCategory::MemoryOrdering,
"#include <stdatomic.h>
int atomic_load_relaxed(_Atomic int* p) {
return atomic_load_explicit(p, memory_order_relaxed);
}",
)
.with_tags(&["atomic", "load", "relaxed"]),
);
suite.add(
X86CodeGenTest::new(
"atomic_load_acquire",
X86TestCategory::MemoryOrdering,
"#include <stdatomic.h>
int atomic_load_acq(_Atomic int* p) {
return atomic_load_explicit(p, memory_order_acquire);
}",
)
.with_tags(&["atomic", "load", "acquire"]),
);
suite.add(
X86CodeGenTest::new(
"atomic_store_release",
X86TestCategory::MemoryOrdering,
"#include <stdatomic.h>
void atomic_store_rel(_Atomic int* p, int v) {
atomic_store_explicit(p, v, memory_order_release);
}",
)
.with_tags(&["atomic", "store", "release"]),
);
suite.add(
X86CodeGenTest::new(
"atomic_store_seq_cst",
X86TestCategory::MemoryOrdering,
"#include <stdatomic.h>
void atomic_store_seq(_Atomic int* p, int v) {
atomic_store_explicit(p, v, memory_order_seq_cst);
}",
)
.with_tags(&["atomic", "store", "seq-cst"]),
);
suite.add(
X86CodeGenTest::new(
"atomic_fetch_add",
X86TestCategory::MemoryOrdering,
"#include <stdatomic.h>
int atomic_fadd(_Atomic int* p, int v) {
return atomic_fetch_add(p, v);
}",
)
.with_tags(&["atomic", "fetch-add"]),
);
suite.add(
X86CodeGenTest::new(
"atomic_fetch_sub",
X86TestCategory::MemoryOrdering,
"#include <stdatomic.h>
int atomic_fsub(_Atomic int* p, int v) {
return atomic_fetch_sub(p, v);
}",
)
.with_tags(&["atomic", "fetch-sub"]),
);
suite.add(
X86CodeGenTest::new(
"atomic_fetch_or",
X86TestCategory::MemoryOrdering,
"#include <stdatomic.h>
int atomic_for(_Atomic int* p, int v) {
return atomic_fetch_or(p, v);
}",
)
.with_tags(&["atomic", "fetch-or"]),
);
suite.add(
X86CodeGenTest::new(
"atomic_fetch_and",
X86TestCategory::MemoryOrdering,
"#include <stdatomic.h>
int atomic_fand(_Atomic int* p, int v) {
return atomic_fetch_and(p, v);
}",
)
.with_tags(&["atomic", "fetch-and"]),
);
suite.add(
X86CodeGenTest::new(
"atomic_fetch_xor",
X86TestCategory::MemoryOrdering,
"#include <stdatomic.h>
int atomic_fxor(_Atomic int* p, int v) {
return atomic_fetch_xor(p, v);
}",
)
.with_tags(&["atomic", "fetch-xor"]),
);
suite.add(
X86CodeGenTest::new(
"atomic_cas_strong",
X86TestCategory::MemoryOrdering,
"#include <stdatomic.h>
int atomic_cas(_Atomic int* p, int expected, int desired) {
int e = expected;
atomic_compare_exchange_strong(p, &e, desired);
return e;
}",
)
.with_tags(&["atomic", "cas"]),
);
suite.add(
X86CodeGenTest::new(
"atomic_cas_weak",
X86TestCategory::MemoryOrdering,
"#include <stdatomic.h>
int atomic_cas_weak(_Atomic int* p, int expected, int desired) {
int e = expected;
atomic_compare_exchange_weak(p, &e, desired);
return e;
}",
)
.with_tags(&["atomic", "cas", "weak"]),
);
suite.add(
X86CodeGenTest::new(
"atomic_exchange",
X86TestCategory::MemoryOrdering,
"#include <stdatomic.h>
int atomic_xchg(_Atomic int* p, int v) {
return atomic_exchange(p, v);
}",
)
.with_tags(&["atomic", "exchange"]),
);
suite.add(
X86CodeGenTest::new(
"atomic_flag_test_and_set",
X86TestCategory::MemoryOrdering,
"#include <stdatomic.h>
int test_flag(atomic_flag* f) {
return atomic_flag_test_and_set(f);
}",
)
.with_tags(&["atomic", "flag"]),
);
suite.add(
X86CodeGenTest::new(
"atomic_flag_clear",
X86TestCategory::MemoryOrdering,
"#include <stdatomic.h>
void clear_flag(atomic_flag* f) {
atomic_flag_clear(f);
}",
)
.with_tags(&["atomic", "flag", "clear"]),
);
suite.add(
X86CodeGenTest::new(
"atomic_thread_fence",
X86TestCategory::MemoryOrdering,
"#include <stdatomic.h>
void do_fence(void) {
atomic_thread_fence(memory_order_seq_cst);
}",
)
.with_tags(&["atomic", "fence"]),
);
suite.add(
X86CodeGenTest::new(
"atomic_signal_fence",
X86TestCategory::MemoryOrdering,
"#include <stdatomic.h>
void do_signal_fence(void) {
atomic_signal_fence(memory_order_acq_rel);
}",
)
.with_tags(&["atomic", "signal-fence"]),
);
suite.add(
X86CodeGenTest::new(
"atomic_is_lock_free",
X86TestCategory::MemoryOrdering,
"#include <stdatomic.h>
int check_lock_free(void) {
_Atomic int x;
return atomic_is_lock_free(&x);
}",
)
.with_tags(&["atomic", "lock-free"]),
);
suite.add(
X86CodeGenTest::new(
"atomic_init",
X86TestCategory::MemoryOrdering,
"#include <stdatomic.h>
int atomic_init_test(void) {
_Atomic int x;
atomic_init(&x, 42);
return atomic_load(&x);
}",
)
.with_tags(&["atomic", "init"]),
);
suite
}
pub fn build_debug_info_suite() -> X86CodeGenTestSuite {
let mut suite =
X86CodeGenTestSuite::new("x86-debug-info", "Tests for debug information generation");
suite.add(
X86CodeGenTest::new(
"dbg_simple_function",
X86TestCategory::DebugInfoTests,
"int add(int a, int b) { return a + b; }",
)
.with_tags(&["debug", "simple"])
.with_flags(&["-g"]),
);
suite.add(
X86CodeGenTest::new(
"dbg_local_variables",
X86TestCategory::DebugInfoTests,
"int compute(int x) { int y = x * 2; int z = y + 1; return z; }",
)
.with_tags(&["debug", "locals"])
.with_flags(&["-g"]),
);
suite.add(
X86CodeGenTest::new(
"dbg_struct_members",
X86TestCategory::DebugInfoTests,
"struct Point { int x; int y; };
int get_x(struct Point p) { return p.x; }",
)
.with_tags(&["debug", "struct"])
.with_flags(&["-g"]),
);
suite.add(
X86CodeGenTest::new(
"dbg_typedef_types",
X86TestCategory::DebugInfoTests,
"typedef int MyInt;
MyInt use_typedef(MyInt x) { return x + 1; }",
)
.with_tags(&["debug", "typedef"])
.with_flags(&["-g"]),
);
suite.add(
X86CodeGenTest::new(
"dbg_enum_types",
X86TestCategory::DebugInfoTests,
"enum Color { RED, GREEN, BLUE };
int use_enum(enum Color c) { return (int)c; }",
)
.with_tags(&["debug", "enum"])
.with_flags(&["-g"]),
);
suite.add(
X86CodeGenTest::new(
"dbg_array_types",
X86TestCategory::DebugInfoTests,
"int sum_array(int arr[10]) {
int s = 0;
for (int i = 0; i < 10; i++) s += arr[i];
return s;
}",
)
.with_tags(&["debug", "array"])
.with_flags(&["-g"]),
);
suite.add(
X86CodeGenTest::new(
"dbg_gline_tables",
X86TestCategory::DebugInfoTests,
"int f(int a) {
int b = a * 2;
int c = b + 3;
return c;
}",
)
.with_tags(&["debug", "line-tables"])
.with_flags(&["-gline-tables-only"]),
);
suite.add(
X86CodeGenTest::new(
"dbg_gfull",
X86TestCategory::DebugInfoTests,
"int g(int x) { return x * x; }",
)
.with_tags(&["debug", "gfull"])
.with_flags(&["-g", "-fdebug-macro"]),
);
suite.add(
X86CodeGenTest::new(
"dbg_inlined_function",
X86TestCategory::DebugInfoTests,
"static inline int square(int x) { return x * x; }
int use_inline(int a) { return square(a); }",
)
.with_tags(&["debug", "inline"])
.with_flags(&["-g", "-O2"]),
);
suite.add(
X86CodeGenTest::new(
"dbg_optimized_variables",
X86TestCategory::DebugInfoTests,
"int opt_debug(int a, int b) {
int tmp = a + b;
int result = tmp * tmp;
return result;
}",
)
.with_tags(&["debug", "optimized"])
.with_flags(&["-g", "-O2"]),
);
suite
}
pub fn build_exception_handling_suite() -> X86CodeGenTestSuite {
let mut suite = X86CodeGenTestSuite::new(
"x86-exception-handling",
"Tests for exception handling and stack unwinding",
);
suite.add(
X86CodeGenTest::new(
"eh_try_catch_int",
X86TestCategory::ExceptionHandling,
"int try_catch_int(int x) {
try {
if (x < 0) throw 42;
return x;
} catch (int e) {
return -e;
}
}",
)
.with_tags(&["eh", "try", "catch"]),
);
suite.add(
X86CodeGenTest::new(
"eh_try_catch_all",
X86TestCategory::ExceptionHandling,
"int catch_all(void) {
try { throw 1; }
catch (...) { return -1; }
return 0;
}",
)
.with_tags(&["eh", "catch-all"]),
);
suite.add(
X86CodeGenTest::new(
"eh_multiple_catch",
X86TestCategory::ExceptionHandling,
"int multi_catch(int type) {
try {
if (type == 1) throw 1;
if (type == 2) throw 'a';
throw 3.0;
} catch (int e) { return e; }
catch (char e) { return (int)e; }
catch (double e) { return (int)e; }
}",
)
.with_tags(&["eh", "multiple-catch"]),
);
suite.add(
X86CodeGenTest::new(
"eh_nested_try",
X86TestCategory::ExceptionHandling,
"int nested_try(void) {
try {
try { throw 1; }
catch (int) { throw 2; }
} catch (int e) {
return e;
}
}",
)
.with_tags(&["eh", "nested"]),
);
suite.add(
X86CodeGenTest::new(
"eh_rethrow",
X86TestCategory::ExceptionHandling,
"int rethrow_test(void) {
try {
try { throw 42; }
catch (int) { throw; }
} catch (int e) {
return e;
}
}",
)
.with_tags(&["eh", "rethrow"]),
);
suite.add(
X86CodeGenTest::new(
"eh_throw_from_dtor",
X86TestCategory::ExceptionHandling,
"struct Guard { ~Guard() { } };
int dtor_test(void) {
Guard g;
try { throw 1; } catch (int) { return -1; }
return 0;
}",
)
.with_tags(&["eh", "dtor"]),
);
suite.add(
X86CodeGenTest::new(
"eh_noexcept_func",
X86TestCategory::ExceptionHandling,
"int noexcept_func(void) noexcept { return 42; }
int call_noexcept(void) { return noexcept_func(); }",
)
.with_tags(&["eh", "noexcept"]),
);
suite.add(
X86CodeGenTest::new(
"eh_throw_in_noexcept",
X86TestCategory::ExceptionHandling,
"int maybe_throw(bool b) {
if (b) throw 1;
return 0;
}",
)
.with_tags(&["eh", "throw"]),
);
suite.add(
X86CodeGenTest::new(
"eh_cleanup_attribute",
X86TestCategory::ExceptionHandling,
"void cleanup_func(int* p) { *p = 0; }
int cleanup_test(void) {
int x __attribute__((cleanup(cleanup_func))) = 42;
return x;
}",
)
.with_tags(&["eh", "cleanup"]),
);
suite.add(
X86CodeGenTest::new(
"eh_setjmp_longjmp",
X86TestCategory::ExceptionHandling,
"#include <setjmp.h>
int setjmp_test(jmp_buf buf) {
if (setjmp(buf) == 0) {
longjmp(buf, 1);
}
return 1;
}",
)
.with_tags(&["eh", "setjmp", "longjmp"]),
);
suite
}
pub fn build_tls_suite() -> X86CodeGenTestSuite {
let mut suite = X86CodeGenTestSuite::new("x86-tls", "Tests for thread-local storage codegen");
suite.add(
X86CodeGenTest::new(
"tls_simple_int",
X86TestCategory::ThreadLocalStorage,
"__thread int tls_counter = 0;
int get_tls(void) { return tls_counter; }",
)
.with_tags(&["tls", "int"]),
);
suite.add(
X86CodeGenTest::new(
"tls_set_value",
X86TestCategory::ThreadLocalStorage,
"__thread int tls_val;
void set_tls(int v) { tls_val = v; }
int get_tls(void) { return tls_val; }",
)
.with_tags(&["tls", "set"]),
);
suite.add(
X86CodeGenTest::new(
"tls_pointer_type",
X86TestCategory::ThreadLocalStorage,
"__thread void* tls_ptr;
void* get_tls_ptr(void) { return tls_ptr; }
void set_tls_ptr(void* p) { tls_ptr = p; }",
)
.with_tags(&["tls", "pointer"]),
);
suite.add(
X86CodeGenTest::new(
"tls_struct_type",
X86TestCategory::ThreadLocalStorage,
"struct Data { int x; double y; };
__thread struct Data tls_data;
int get_tls_x(void) { return tls_data.x; }",
)
.with_tags(&["tls", "struct"]),
);
suite.add(
X86CodeGenTest::new(
"tls_array_type",
X86TestCategory::ThreadLocalStorage,
"__thread int tls_array[10];
int get_tls_elem(int i) { return tls_array[i]; }",
)
.with_tags(&["tls", "array"]),
);
suite.add(
X86CodeGenTest::new(
"tls_static_local",
X86TestCategory::ThreadLocalStorage,
"int counter(void) {
static __thread int c = 0;
return c++;
}",
)
.with_tags(&["tls", "static"]),
);
suite.add(
X86CodeGenTest::new(
"tls_extern",
X86TestCategory::ThreadLocalStorage,
"extern __thread int ext_tls;
int get_ext_tls(void) { return ext_tls; }",
)
.with_tags(&["tls", "extern"]),
);
suite
}
pub fn build_linkage_suite() -> X86CodeGenTestSuite {
let mut suite =
X86CodeGenTestSuite::new("x86-linkage", "Tests for symbol linkage and visibility");
suite.add(
X86CodeGenTest::new(
"linkage_static_function",
X86TestCategory::Linkage,
"static int private_func(int x) { return x * 2; }
int public_func(int x) { return private_func(x); }",
)
.with_tags(&["linkage", "static"]),
);
suite.add(
X86CodeGenTest::new(
"linkage_extern_function",
X86TestCategory::Linkage,
"extern int extern_func(int x);
int call_extern(int x) { return extern_func(x); }",
)
.with_tags(&["linkage", "extern"]),
);
suite.add(
X86CodeGenTest::new(
"linkage_weak_function",
X86TestCategory::Linkage,
"__attribute__((weak)) int weak_func(int x) { return x; }",
)
.with_tags(&["linkage", "weak"]),
);
suite.add(
X86CodeGenTest::new(
"linkage_weak_alias",
X86TestCategory::Linkage,
"int original_func(int x) { return x; }
int alias_func(int x) __attribute__((alias(\"original_func\")));",
)
.with_tags(&["linkage", "alias"]),
);
suite.add(
X86CodeGenTest::new(
"linkage_hidden_visibility",
X86TestCategory::Linkage,
"__attribute__((visibility(\"hidden\"))) int hidden_func(int x) { return x; }",
)
.with_tags(&["linkage", "visibility", "hidden"]),
);
suite.add(
X86CodeGenTest::new(
"linkage_protected_visibility",
X86TestCategory::Linkage,
"__attribute__((visibility(\"protected\"))) int prot_func(int x) { return x; }",
)
.with_tags(&["linkage", "visibility", "protected"]),
);
suite.add(
X86CodeGenTest::new(
"linkage_default_visibility",
X86TestCategory::Linkage,
"__attribute__((visibility(\"default\"))) int def_func(int x) { return x; }",
)
.with_tags(&["linkage", "visibility", "default"]),
);
suite.add(
X86CodeGenTest::new(
"linkage_internal_visibility",
X86TestCategory::Linkage,
"__attribute__((visibility(\"internal\"))) int intern_func(int x) { return x; }",
)
.with_tags(&["linkage", "visibility", "internal"]),
);
suite.add(
X86CodeGenTest::new(
"linkage_common_variable",
X86TestCategory::Linkage,
"int common_var;
int get_common(void) { return common_var; }",
)
.with_tags(&["linkage", "common"]),
);
suite.add(
X86CodeGenTest::new(
"linkage_tentative_definition",
X86TestCategory::Linkage,
"int tent_var;
int tent_var;
int get_tent(void) { return tent_var; }",
)
.with_tags(&["linkage", "tentative"]),
);
suite
}
pub fn build_attributes_suite() -> X86CodeGenTestSuite {
let mut suite =
X86CodeGenTestSuite::new("x86-attributes", "Tests for function/variable attributes");
suite.add(
X86CodeGenTest::new(
"attr_aligned_variable",
X86TestCategory::Attributes,
"int aligned_var __attribute__((aligned(64)));
int* get_aligned(void) { return &aligned_var; }",
)
.with_tags(&["attr", "aligned"]),
);
suite.add(
X86CodeGenTest::new(
"attr_packed_struct",
X86TestCategory::Attributes,
"struct __attribute__((packed)) Packed {
char a;
int b;
short c;
};
int packed_size(void) { return sizeof(struct Packed); }",
)
.with_tags(&["attr", "packed"]),
);
suite.add(
X86CodeGenTest::new(
"attr_section_variable",
X86TestCategory::Attributes,
"int section_var __attribute__((section(\".my_data\"))) = 42;
int get_section_var(void) { return section_var; }",
)
.with_tags(&["attr", "section"]),
);
suite.add(
X86CodeGenTest::new(
"attr_section_function",
X86TestCategory::Attributes,
"__attribute__((section(\".my_text\"))) int section_func(int x) { return x; }",
)
.with_tags(&["attr", "section", "function"]),
);
suite.add(
X86CodeGenTest::new(
"attr_constructor_function",
X86TestCategory::Attributes,
"static int init_val;
__attribute__((constructor)) void my_init(void) { init_val = 42; }
int get_init_val(void) { return init_val; }",
)
.with_tags(&["attr", "constructor"]),
);
suite.add(
X86CodeGenTest::new(
"attr_destructor_function",
X86TestCategory::Attributes,
"__attribute__((destructor)) void my_fini(void) { }",
)
.with_tags(&["attr", "destructor"]),
);
suite.add(
X86CodeGenTest::new(
"attr_constructor_priority",
X86TestCategory::Attributes,
"__attribute__((constructor(101))) void early_init(void) { }
__attribute__((constructor(102))) void late_init(void) { }",
)
.with_tags(&["attr", "constructor", "priority"]),
);
suite.add(
X86CodeGenTest::new(
"attr_used_variable",
X86TestCategory::Attributes,
"__attribute__((used)) static int keep_me = 42;",
)
.with_tags(&["attr", "used"]),
);
suite.add(
X86CodeGenTest::new(
"attr_unused_parameter",
X86TestCategory::Attributes,
"int foo(int x, int y __attribute__((unused))) { return x; }",
)
.with_tags(&["attr", "unused"]),
);
suite.add(X86CodeGenTest::new(
"attr_deprecated_function",
X86TestCategory::Attributes,
"__attribute__((deprecated(\"use new_func instead\"))) int old_func(int x) { return x; }"
).with_tags(&["attr", "deprecated"]));
suite.add(
X86CodeGenTest::new(
"attr_malloc_function",
X86TestCategory::Attributes,
"__attribute__((malloc)) void* my_malloc(unsigned long sz) {
extern void* malloc(unsigned long);
return malloc(sz);
}",
)
.with_tags(&["attr", "malloc"]),
);
suite.add(
X86CodeGenTest::new(
"attr_returns_nonnull",
X86TestCategory::Attributes,
"__attribute__((returns_nonnull)) void* must_return(void) {
static int x;
return &x;
}",
)
.with_tags(&["attr", "returns-nonnull"]),
);
suite.add(
X86CodeGenTest::new(
"attr_format_printf",
X86TestCategory::Attributes,
"__attribute__((format(printf, 1, 2)))
void my_printf(const char* fmt, ...);
void use_printf(void) { my_printf(\"hello %d\", 42); }",
)
.with_tags(&["attr", "format"]),
);
suite.add(
X86CodeGenTest::new(
"attr_cold_function",
X86TestCategory::Attributes,
"__attribute__((cold)) void cold_path(void) { }
void hot_path(void) { if (0) cold_path(); }",
)
.with_tags(&["attr", "cold"]),
);
suite.add(
X86CodeGenTest::new(
"attr_hot_function",
X86TestCategory::Attributes,
"__attribute__((hot)) int hot_func(int x) { return x * x; }",
)
.with_tags(&["attr", "hot"]),
);
suite.add(
X86CodeGenTest::new(
"attr_flatten_function",
X86TestCategory::Attributes,
"static int helper(int x) { return x * 2; }
__attribute__((flatten)) int outer(int x) { return helper(x); }",
)
.with_tags(&["attr", "flatten"]),
);
suite.add(
X86CodeGenTest::new(
"attr_target_function",
X86TestCategory::Attributes,
"__attribute__((target(\"sse4.2\"))) int sse42_func(int x) { return x; }
__attribute__((target(\"avx2\"))) int avx2_func(int x) { return x; }",
)
.with_tags(&["attr", "target"]),
);
suite.add(
X86CodeGenTest::new(
"attr_target_clones",
X86TestCategory::Attributes,
"__attribute__((target_clones(\"default\", \"sse4.2\", \"avx2\")))
int dispatch_func(int x) { return x * x; }",
)
.with_tags(&["attr", "target-clones"]),
);
suite.add(
X86CodeGenTest::new(
"attr_ifunc_resolver",
X86TestCategory::Attributes,
"int impl_default(int x) { return x; }
int impl_sse42(int x) { return x * 2; }
void* resolve_impl(void) __attribute__((ifunc(\"resolve_impl\")));",
)
.with_tags(&["attr", "ifunc"]),
);
suite.add(
X86CodeGenTest::new(
"attr_mode_di",
X86TestCategory::Attributes,
"typedef int di_int __attribute__((mode(DI)));
di_int mode_test(di_int a, di_int b) { return a + b; }",
)
.with_tags(&["attr", "mode"]),
);
suite.add(
X86CodeGenTest::new(
"attr_mode_si",
X86TestCategory::Attributes,
"typedef int si_int __attribute__((mode(SI)));
si_int si_mode_test(si_int a) { return a; }",
)
.with_tags(&["attr", "mode", "SI"]),
);
suite
}
pub fn build_sanitizers_suite() -> X86CodeGenTestSuite {
let mut suite = X86CodeGenTestSuite::new(
"x86-sanitizers",
"Tests for sanitizer instrumentation codegen",
);
suite.add(
X86CodeGenTest::new(
"san_address_basic",
X86TestCategory::Sanitizers,
"int asan_test(void) {
int x = 42;
return x;
}",
)
.with_tags(&["sanitizer", "asan"])
.with_flags(&["-fsanitize=address"]),
);
suite.add(
X86CodeGenTest::new(
"san_undefined_basic",
X86TestCategory::Sanitizers,
"int ubsan_test(int x) {
return x + 1;
}",
)
.with_tags(&["sanitizer", "ubsan"])
.with_flags(&["-fsanitize=undefined"]),
);
suite.add(
X86CodeGenTest::new(
"san_undefined_integer",
X86TestCategory::Sanitizers,
"int ubsan_signed(int a, int b) {
return a + b;
}",
)
.with_tags(&["sanitizer", "ubsan", "signed-overflow"])
.with_flags(&["-fsanitize=signed-integer-overflow"]),
);
suite.add(
X86CodeGenTest::new(
"san_undefined_shift",
X86TestCategory::Sanitizers,
"int shift_test(int a, int b) {
return a << b;
}",
)
.with_tags(&["sanitizer", "ubsan", "shift"])
.with_flags(&["-fsanitize=shift"]),
);
suite.add(
X86CodeGenTest::new(
"san_undefined_bounds",
X86TestCategory::Sanitizers,
"int bounds_test(int* arr, int i) {
return arr[i];
}",
)
.with_tags(&["sanitizer", "ubsan", "bounds"])
.with_flags(&["-fsanitize=bounds"]),
);
suite.add(
X86CodeGenTest::new(
"san_undefined_alignment",
X86TestCategory::Sanitizers,
"int align_test(int* p) {
return *p;
}",
)
.with_tags(&["sanitizer", "ubsan", "alignment"])
.with_flags(&["-fsanitize=alignment"]),
);
suite.add(
X86CodeGenTest::new(
"san_undefined_null",
X86TestCategory::Sanitizers,
"int null_test(int* p) {
return *p;
}",
)
.with_tags(&["sanitizer", "ubsan", "null"])
.with_flags(&["-fsanitize=null"]),
);
suite.add(
X86CodeGenTest::new(
"san_thread_basic",
X86TestCategory::Sanitizers,
"int tsan_test(int* p) {
return *p;
}",
)
.with_tags(&["sanitizer", "tsan"])
.with_flags(&["-fsanitize=thread"]),
);
suite.add(
X86CodeGenTest::new(
"san_memory_basic",
X86TestCategory::Sanitizers,
"int msan_test(int x) {
int y = x;
return y;
}",
)
.with_tags(&["sanitizer", "msan"])
.with_flags(&["-fsanitize=memory"]),
);
suite.add(
X86CodeGenTest::new(
"san_leak_basic",
X86TestCategory::Sanitizers,
"#include <stdlib.h>
int lsan_test(void) {
void* p = malloc(100);
return p != 0;
}",
)
.with_tags(&["sanitizer", "lsan"])
.with_flags(&["-fsanitize=leak"]),
);
suite.add(
X86CodeGenTest::new(
"san_cfi_vcall",
X86TestCategory::Sanitizers,
"struct Base { virtual void f() {} };
struct Derived : Base { void f() override {} };
void call_virtual(Base* b) { b->f(); }",
)
.with_tags(&["sanitizer", "cfi"])
.with_flags(&["-fsanitize=cfi-vcall"]),
);
suite.add(
X86CodeGenTest::new(
"san_safe_stack",
X86TestCategory::Sanitizers,
"int safe_stack_test(void) {
int buf[10];
buf[0] = 42;
return buf[0];
}",
)
.with_tags(&["sanitizer", "safe-stack"])
.with_flags(&["-fsanitize=safe-stack"]),
);
suite.add(
X86CodeGenTest::new(
"san_shadow_call_stack",
X86TestCategory::Sanitizers,
"int shadow_stack_test(int x) {
return x;
}",
)
.with_tags(&["sanitizer", "shadow-call-stack"])
.with_flags(&["-fsanitize=shadow-call-stack"]),
);
suite
}
pub fn build_profiling_suite() -> X86CodeGenTestSuite {
let mut suite = X86CodeGenTestSuite::new(
"x86-profiling",
"Tests for profiling instrumentation codegen",
);
suite.add(
X86CodeGenTest::new(
"prof_instr_generate",
X86TestCategory::Profiling,
"int prof_test(int x) {
if (x > 10) return x * 2;
return x;
}",
)
.with_tags(&["profiling", "instr"])
.with_flags(&["-fprofile-instr-generate"]),
);
suite.add(
X86CodeGenTest::new(
"prof_generate",
X86TestCategory::Profiling,
"int gcov_test(int a, int b) {
return a > b ? a : b;
}",
)
.with_tags(&["profiling", "gcov"])
.with_flags(&["-fprofile-generate"]),
);
suite.add(
X86CodeGenTest::new(
"prof_arcs",
X86TestCategory::Profiling,
"int arcs_test(int x) {
int s = 0;
for (int i = 0; i < x; i++) s += i;
return s;
}",
)
.with_tags(&["profiling", "arcs"])
.with_flags(&["-fprofile-arcs"]),
);
suite.add(
X86CodeGenTest::new(
"prof_coverage_mapping",
X86TestCategory::Profiling,
"int cov_test(int x) {
if (x > 0) return 1;
else if (x < 0) return -1;
return 0;
}",
)
.with_tags(&["profiling", "coverage"])
.with_flags(&["-fcoverage-mapping"]),
);
suite.add(
X86CodeGenTest::new(
"prof_value_profiling",
X86TestCategory::Profiling,
"int switch_test(int x) {
switch (x) {
case 1: return 10;
case 2: return 20;
default: return 0;
}
}",
)
.with_tags(&["profiling", "value"])
.with_flags(&["-fprofile-instr-generate"]),
);
suite
}
pub fn build_lto_suite() -> X86CodeGenTestSuite {
let mut suite = X86CodeGenTestSuite::new("x86-lto", "Tests for Link-Time Optimization codegen");
suite.add(
X86CodeGenTest::new(
"lto_cross_module_inline",
X86TestCategory::LTO,
"int helper(int x) { return x * 2; }
int caller(int a) { return helper(a) + helper(a + 1); }",
)
.with_tags(&["lto", "inline"])
.with_flags(&["-flto"]),
);
suite.add(
X86CodeGenTest::new(
"lto_thin_cross_module",
X86TestCategory::LTO,
"int shared_func(int x) { return x * x; }
int use_shared(int a) { return shared_func(a); }",
)
.with_tags(&["lto", "thin"])
.with_flags(&["-flto=thin"]),
);
suite.add(
X86CodeGenTest::new(
"lto_linkonce_odr",
X86TestCategory::LTO,
"inline int odr_func(int x) { return x + 1; }
int use_odr(int a) { return odr_func(a); }",
)
.with_tags(&["lto", "odr"])
.with_flags(&["-flto"]),
);
suite.add(
X86CodeGenTest::new(
"lto_visibility_hidden",
X86TestCategory::LTO,
"__attribute__((visibility(\"hidden\")))
int lto_hidden(int x) { return x; }",
)
.with_tags(&["lto", "visibility"])
.with_flags(&["-flto"]),
);
suite.add(
X86CodeGenTest::new(
"lto_internalize",
X86TestCategory::LTO,
"static int internal_func(int x) { return x; }
int public_api(int x) { return internal_func(x); }",
)
.with_tags(&["lto", "internalize"])
.with_flags(&["-flto"]),
);
suite
}
pub fn build_roundtrip_suite() -> X86CodeGenTestSuite {
let mut suite = X86CodeGenTestSuite::new("x86-roundtrip", "Roundtrip verification tests");
let sources = [
("rt_simple", "int f(void) { return 42; }"),
("rt_add", "int g(int a, int b) { return a + b; }"),
(
"rt_loop",
"int sum(int n) { int s=0; for(int i=0;i<n;i++)s+=i; return s; }",
),
(
"rt_switch",
"int sw(int x) { switch(x){case 0:return 1;case 1:return 2;default:return 0;} }",
),
("rt_ptr", "int load(int* p) { return *p; }"),
(
"rt_struct",
"struct S{int a;}; int get(struct S s) { return s.a; }",
),
(
"rt_float",
"double mul(double a, double b) { return a * b; }",
),
("rt_ternary", "int max(int a, int b) { return a>b?a:b; }"),
(
"rt_while",
"int cnt(int n) { int i=0; while(i<n) i++; return i; }",
),
(
"rt_do_while",
"int cnt2(int n) { int i=0; do{i++;}while(i<n); return i; }",
),
(
"rt_goto",
"int gt(int x) { if(x>0) goto pos; return -1; pos: return 1; }",
),
("rt_bitwise", "int bw(int a, int b) { return (a&b)|(a^b); }"),
("rt_shift", "int sh(int a, int b) { return (a<<b)|(a>>b); }"),
("rt_neg", "int neg(int a) { return -a; }"),
("rt_not", "int not_op(int a) { return ~a; }"),
];
for (name, source) in &sources {
suite.add(
X86CodeGenTest::new(name, X86TestCategory::Roundtrip, source)
.with_verification(X86VerificationTarget::Roundtrip)
.with_tags(&["roundtrip"]),
);
}
suite
}
pub fn build_differential_suite() -> X86CodeGenTestSuite {
let mut suite = X86CodeGenTestSuite::new(
"x86-differential",
"Differential verification against system compiler",
);
let sources = [
("diff_arith", "int f(int a, int b) { return a+b; }"),
("diff_logic", "int g(int a, int b) { return a&&b||!a; }"),
(
"diff_loop",
"int h(int n) { int s=0; for(int i=0;i<n;i++)s+=i; return s; }",
),
(
"diff_struct",
"struct P{int x,y;}; int d(struct P p){ return p.x+p.y; }",
),
("diff_ptr", "int e(int* p, int n) { return p[n]; }"),
("diff_float", "float j(float x) { return x*x; }"),
("diff_cast", "int k(float x) { return (int)x; }"),
(
"diff_tail",
"int l(int n, int a) { return n?l(n-1,a+n):a; }",
),
(
"diff_switch",
"int m(int x) { switch(x){case 0:return 2;case 3:return 5;default:return 7;} }",
),
("diff_ternary", "int n(int a, int b) { return a>b?a:b; }"),
];
for (name, source) in &sources {
suite.add(
X86CodeGenTest::new(name, X86TestCategory::Differential, source)
.with_verification(X86VerificationTarget::Differential)
.with_tags(&["differential"]),
);
}
suite
}
pub fn build_all_x86_codegen_suites_extended() -> Vec<X86CodeGenTestSuite> {
let mut suites = build_all_x86_codegen_suites();
suites.push(build_inline_asm_suite());
suites.push(build_vector_ops_suite());
suites.push(build_memory_ordering_suite());
suites.push(build_debug_info_suite());
suites.push(build_exception_handling_suite());
suites.push(build_tls_suite());
suites.push(build_linkage_suite());
suites.push(build_attributes_suite());
suites.push(build_sanitizers_suite());
suites.push(build_profiling_suite());
suites.push(build_lto_suite());
suites.push(build_roundtrip_suite());
suites.push(build_differential_suite());
suites.push(build_stress_test_suite());
suites
}
#[derive(Debug, Clone)]
pub struct DeltaDebugger {
pub original: String,
pub current: String,
pub test_count: usize,
pub granularity: usize,
}
impl DeltaDebugger {
pub fn new(source: &str) -> Self {
Self {
original: source.to_string(),
current: source.to_string(),
test_count: 0,
granularity: 2,
}
}
pub fn minimize(&mut self, oracle: fn(&str) -> bool) -> String {
let mut changed = true;
let mut n = 2;
while n <= self.current.len() && changed {
changed = false;
let chunk_size = self.current.len() / n;
if chunk_size == 0 {
break;
}
for i in 0..n {
let start = i * chunk_size;
let end = if i + 1 == n {
self.current.len()
} else {
(i + 1) * chunk_size
};
let mut candidate = String::with_capacity(self.current.len() - (end - start));
candidate.push_str(&self.current[..start]);
candidate.push_str(&self.current[end..]);
self.test_count += 1;
if oracle(&candidate) {
self.current = candidate;
changed = true;
n = std::cmp::max(n - 1, 2);
break;
}
}
if !changed {
n *= 2;
if n > self.current.len() {
break;
}
}
}
self.current.clone()
}
pub fn minimize_chars(&mut self, oracle: fn(&str) -> bool) -> String {
let mut i = 0;
while i < self.current.len() {
let mut candidate = String::with_capacity(self.current.len() - 1);
candidate.push_str(&self.current[..i]);
candidate.push_str(&self.current[i + 1..]);
self.test_count += 1;
if oracle(&candidate) {
self.current = candidate;
} else {
i += 1;
}
}
self.current.clone()
}
pub fn minimize_lines(&mut self, oracle: fn(&str) -> bool) -> String {
let mut i = 0;
let mut lines: Vec<String> = self.current.lines().map(|s| s.to_string()).collect();
while i < lines.len() {
let mut candidate_lines = lines.clone();
candidate_lines.remove(i);
let candidate = candidate_lines.join("\n");
self.test_count += 1;
if oracle(&candidate) {
lines = candidate_lines;
} else {
i += 1;
}
}
self.current = lines.join("\n");
self.current.clone()
}
pub fn stats(&self) -> (usize, usize, usize) {
(self.original.len(), self.current.len(), self.test_count)
}
pub fn reduction_ratio(&self) -> f64 {
if self.original.is_empty() {
return 1.0;
}
self.current.len() as f64 / self.original.len() as f64
}
}
impl X86RngState {
pub fn next_i8(&mut self) -> i8 {
self.next_u32() as i8
}
pub fn next_i16(&mut self) -> i16 {
self.next_u32() as i16
}
pub fn next_i32(&mut self) -> i32 {
self.next_u32() as i32
}
pub fn next_i64(&mut self) -> i64 {
self.next_u64() as i64
}
pub fn next_f32(&mut self) -> f32 {
(self.next_u32() as f32) / (u32::MAX as f32)
}
pub fn next_f64(&mut self) -> f64 {
(self.next_u64() as f64) / (u64::MAX as f64)
}
pub fn next_bool(&mut self) -> bool {
(self.next_u32() & 1) == 1
}
pub fn next_byte(&mut self) -> u8 {
self.next_u32() as u8
}
pub fn next_bytes(&mut self, len: usize) -> Vec<u8> {
(0..len).map(|_| self.next_byte()).collect()
}
pub fn next_alpha(&mut self) -> char {
if self.next_bool() {
(b'a' + (self.next_byte() % 26)) as char
} else {
(b'A' + (self.next_byte() % 26)) as char
}
}
pub fn next_alnum(&mut self) -> char {
match self.next_u32() % 3 {
0 => self.next_alpha(),
1 => (b'0' + (self.next_byte() % 10)) as char,
_ => self.next_alpha(),
}
}
pub fn next_string(&mut self, len: usize) -> String {
(0..len).map(|_| self.next_alnum()).collect()
}
pub fn next_c_identifier(&mut self) -> String {
let len = self.range(2, 16);
let mut s = String::with_capacity(len);
s.push(if self.next_bool() {
'_'
} else {
self.next_alpha().to_ascii_lowercase()
});
for _ in 1..len {
s.push(self.next_alnum().to_ascii_lowercase());
if self.next_u32() % 5 == 0 {
s.push('_');
}
}
s
}
}
impl X86TestGenerator {
pub fn generate_function(&mut self, ret_type: &str, param_count: usize) -> String {
let name = format!("func_{}", self.tests_generated);
self.tests_generated += 1;
let mut params = Vec::new();
for _ in 0..param_count {
let ptype = self.gen_type();
let pname = self.gen_identifier();
params.push(format!("{} {}", ptype, pname));
}
let body = self.gen_function_body(Self::infer_return_type(ret_type));
format!(
"{} {}({}) {{\n{}}}\n",
ret_type,
name,
params.join(", "),
body
)
}
pub fn generate_struct(&mut self, field_count: usize) -> String {
let name = format!("Struct{}", self.tests_generated);
self.tests_generated += 1;
let mut buf = format!("struct {} {{\n", name);
for _ in 0..field_count {
let ftype = self.gen_type();
let fname = self.gen_identifier();
buf.push_str(&format!(" {} {};\n", ftype, fname));
}
buf.push_str("};\n");
buf
}
pub fn generate_translation_unit(&mut self) -> String {
let mut tu = String::new();
tu.push_str("#include <stdint.h>\n");
tu.push_str("#include <stddef.h>\n");
tu.push_str("#include <stdbool.h>\n\n");
let num_fwd = self.rng.range(0, 5);
for _ in 0..num_fwd {
let ret = self.gen_type();
let name = self.gen_identifier();
let nparams = self.rng.range(0, 4);
let mut params = Vec::new();
for _ in 0..nparams {
params.push(format!("{} {}", self.gen_type(), self.gen_identifier()));
}
tu.push_str(&format!("{} {}({});\n", ret, name, params.join(", ")));
}
if self.config.include_structs {
let num_structs = self.rng.range(0, 3);
for _ in 0..num_structs {
let nfields = self.rng.range(1, 6);
tu.push_str(&self.generate_struct(nfields));
}
}
let num_globals = self.rng.range(0, 3);
for _ in 0..num_globals {
let gtype = self.gen_type();
let gname = self.gen_identifier();
if self.rng.next_bool() {
tu.push_str(&format!(
"static {} {} = {};\n",
gtype,
gname,
self.rng.next_i32()
));
} else {
tu.push_str(&format!("{} {} = {};\n", gtype, gname, self.rng.next_i32()));
}
}
let num_funcs = self.rng.range(1, self.config.max_functions);
for _ in 0..num_funcs {
let ret = self.gen_type();
let nparams = self.rng.range(0, 5);
tu.push_str(&self.generate_function(&ret, nparams));
}
tu.push_str("int main(void) {\n");
tu.push_str(" return 0;\n");
tu.push_str("}\n");
tu
}
pub fn generate_batch(
&mut self,
count: usize,
category: X86TestCategory,
) -> Vec<X86CodeGenTest> {
(0..count).map(|_| self.generate_test(category)).collect()
}
}
impl X86CodeGenBenchmark {
pub fn compare(&self, a: &X86BenchmarkMeasurement, b: &X86BenchmarkMeasurement) -> String {
let mut report = String::new();
report.push_str(&format!("Comparing '{}' vs '{}':\n", a.name, b.name));
let time_diff = (a.total_time.as_nanos() as f64 - b.total_time.as_nanos() as f64).abs();
report.push_str(&format!(" Time diff: {:.2}ms\n", time_diff / 1_000_000.0));
let size_diff = (a.binary_size as i64 - b.binary_size as i64).abs();
report.push_str(&format!(" Size diff: {} bytes\n", size_diff));
let inst_diff = (a.instruction_count as i64 - b.instruction_count as i64).abs();
report.push_str(&format!(" Instr diff: {}\n", inst_diff));
report
}
pub fn fastest(&self) -> Option<&X86BenchmarkMeasurement> {
self.measurements.iter().min_by_key(|m| m.total_time)
}
pub fn smallest_binary(&self) -> Option<&X86BenchmarkMeasurement> {
self.measurements.iter().min_by_key(|m| m.binary_size)
}
pub fn fewest_instructions(&self) -> Option<&X86BenchmarkMeasurement> {
self.measurements.iter().min_by_key(|m| m.instruction_count)
}
pub fn median_time(&self) -> Duration {
if self.measurements.is_empty() {
return Duration::ZERO;
}
let mut times: Vec<Duration> = self.measurements.iter().map(|m| m.total_time).collect();
times.sort();
let mid = times.len() / 2;
if times.len() % 2 == 0 {
(times[mid - 1] + times[mid]) / 2
} else {
times[mid]
}
}
pub fn time_stddev(&self) -> Duration {
if self.measurements.len() < 2 {
return Duration::ZERO;
}
let mean = self.average_time().as_nanos() as f64;
let variance: f64 = self
.measurements
.iter()
.map(|m| {
let diff = m.total_time.as_nanos() as f64 - mean;
diff * diff
})
.sum::<f64>()
/ (self.measurements.len() - 1) as f64;
Duration::from_nanos(variance.sqrt() as u64)
}
pub fn to_csv(&self) -> String {
let mut csv = String::from(
"name,total_time_ns,binary_size,ir_size,asm_size,instruction_count,basic_block_count\n",
);
for m in &self.measurements {
csv.push_str(&format!(
"{},{},{},{},{},{},{}\n",
m.name,
m.total_time.as_nanos(),
m.binary_size,
m.ir_size,
m.asm_size,
m.instruction_count,
m.basic_block_count
));
}
csv
}
pub fn to_json(&self) -> String {
let mut json = String::from("[\n");
for (i, m) in self.measurements.iter().enumerate() {
if i > 0 {
json.push_str(",\n");
}
json.push_str(&format!(
" {{\"name\":\"{}\",\"total_time_ns\":{},\"binary_size\":{},\"ir_size\":{},\"asm_size\":{},\"instruction_count\":{},\"basic_block_count\":{}}}",
m.name,
m.total_time.as_nanos(),
m.binary_size,
m.ir_size,
m.asm_size,
m.instruction_count,
m.basic_block_count
));
}
json.push_str("\n]");
json
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_create_codegen_test() {
let test = X86CodeGenTest::new(
"test1",
X86TestCategory::IntegerOps,
"int f() { return 0; }",
);
assert_eq!(test.name, "test1");
assert_eq!(test.category, X86TestCategory::IntegerOps);
assert!(test.should_compile);
assert!(!test.is_xfail);
}
#[test]
fn test_codegen_test_with_return() {
let test = X86CodeGenTest::new("t", X86TestCategory::IntegerOps, "int f() { return 42; }")
.with_return(42);
assert_eq!(test.expected_return, Some(42));
}
#[test]
fn test_codegen_test_with_stdout() {
let test = X86CodeGenTest::new("t", X86TestCategory::IntegerOps, "int f() { return 0; }")
.with_stdout("hello");
assert_eq!(test.expected_stdout, Some("hello".to_string()));
}
#[test]
fn test_codegen_test_xfail() {
let test =
X86CodeGenTest::new("t", X86TestCategory::IntegerOps, "bad code").xfail("known issue");
assert!(test.is_xfail);
assert_eq!(test.xfail_reason, Some("known issue".to_string()));
}
#[test]
fn test_codegen_test_flags() {
let test = X86CodeGenTest::new("t", X86TestCategory::IntegerOps, "int f() { return 0; }")
.with_flags(&["-O2", "-Wall"]);
assert_eq!(test.flags, vec!["-O2", "-Wall"]);
}
#[test]
fn test_codegen_test_expected_ir() {
let test = X86CodeGenTest::new("t", X86TestCategory::IntegerOps, "int f() { return 0; }")
.with_expected_ir("ret i32 0");
assert_eq!(test.expected_ir, vec!["ret i32 0"]);
}
#[test]
fn test_codegen_test_expected_asm() {
let test = X86CodeGenTest::new("t", X86TestCategory::IntegerOps, "int f() { return 0; }")
.with_expected_asm("ret");
assert_eq!(test.expected_asm, vec!["ret"]);
}
#[test]
fn test_codegen_test_opt_level() {
let test = X86CodeGenTest::new("t", X86TestCategory::IntegerOps, "int f() { return 0; }")
.with_opt_level(X86OptLevel::O3);
assert_eq!(test.opt_level, X86OptLevel::O3);
}
#[test]
fn test_codegen_test_tags() {
let test = X86CodeGenTest::new("t", X86TestCategory::IntegerOps, "int f() { return 0; }")
.with_tags(&["fast", "regression"]);
assert_eq!(test.tags, vec!["fast", "regression"]);
}
#[test]
fn test_codegen_test_features() {
let test = X86CodeGenTest::new("t", X86TestCategory::X86Builtins, "int f() { return 0; }")
.with_features(&["avx2", "sse4.1"]);
assert_eq!(test.required_features, vec!["avx2", "sse4.1"]);
}
#[test]
fn test_codegen_test_time_budget() {
let test = X86CodeGenTest::new("t", X86TestCategory::IntegerOps, "int f() { return 0; }")
.with_time_budget(100);
assert_eq!(test.time_budget_ms, Some(100));
}
#[test]
fn test_codegen_test_size_budget() {
let test = X86CodeGenTest::new("t", X86TestCategory::IntegerOps, "int f() { return 0; }")
.with_size_budget(1024);
assert_eq!(test.size_budget_bytes, Some(1024));
}
#[test]
fn test_codegen_test_effective_triple() {
let test = X86CodeGenTest::new("t", X86TestCategory::IntegerOps, "int f() { return 0; }");
assert_eq!(test.effective_triple(), "x86_64-unknown-linux-gnu");
}
#[test]
fn test_codegen_test_custom_triple() {
let mut test =
X86CodeGenTest::new("t", X86TestCategory::IntegerOps, "int f() { return 0; }");
test.target_triple = Some("i386-unknown-linux-gnu".to_string());
assert_eq!(test.effective_triple(), "i386-unknown-linux-gnu");
}
#[test]
fn test_suite_creation() {
let suite = X86CodeGenTestSuite::new("my-suite", "A test suite");
assert_eq!(suite.name, "my-suite");
assert_eq!(suite.description, "A test suite");
assert!(suite.is_empty());
}
#[test]
fn test_suite_add_test() {
let mut suite = X86CodeGenTestSuite::new("s", "d");
suite.add(X86CodeGenTest::new(
"t1",
X86TestCategory::IntegerOps,
"int f() { return 0; }",
));
assert_eq!(suite.len(), 1);
}
#[test]
fn test_suite_add_many() {
let mut suite = X86CodeGenTestSuite::new("s", "d");
suite.add_many(vec![
X86CodeGenTest::new("t1", X86TestCategory::IntegerOps, "int f() { return 0; }"),
X86CodeGenTest::new("t2", X86TestCategory::IntegerOps, "int g() { return 1; }"),
]);
assert_eq!(suite.len(), 2);
}
#[test]
fn test_suite_filter_by_tag() {
let mut suite = X86CodeGenTestSuite::new("s", "d");
suite.add(
X86CodeGenTest::new("t1", X86TestCategory::IntegerOps, "int f() { return 0; }")
.with_tags(&["fast"]),
);
suite.add(
X86CodeGenTest::new("t2", X86TestCategory::IntegerOps, "int g() { return 1; }")
.with_tags(&["slow"]),
);
assert_eq!(suite.filter_by_tag("fast").len(), 1);
assert_eq!(suite.filter_by_tag("slow").len(), 1);
assert_eq!(suite.filter_by_tag("nonexistent").len(), 0);
}
#[test]
fn test_suite_filter_by_category() {
let mut suite = X86CodeGenTestSuite::new("s", "d");
suite.add(X86CodeGenTest::new(
"t1",
X86TestCategory::IntegerOps,
"int f() { return 0; }",
));
suite.add(X86CodeGenTest::new(
"t2",
X86TestCategory::FloatingPoint,
"float g() { return 0; }",
));
assert_eq!(
suite.filter_by_category(X86TestCategory::IntegerOps).len(),
1
);
assert_eq!(
suite
.filter_by_category(X86TestCategory::FloatingPoint)
.len(),
1
);
}
#[test]
fn test_verifier_creation() {
let v = X86CodeGenVerifier::new();
assert!(v.verify_ir);
assert!(v.verify_asm);
assert!(v.verify_obj);
assert!(!v.do_roundtrip);
}
#[test]
fn test_verifier_full_mode() {
let v = X86CodeGenVerifier::new().full_verification();
assert!(v.do_roundtrip);
}
#[test]
fn test_verifier_with_system_cc() {
let v = X86CodeGenVerifier::new().with_system_cc("/usr/bin/gcc");
assert!(v.do_differential);
assert_eq!(v.system_cc, Some("/usr/bin/gcc".to_string()));
}
#[test]
fn test_verifier_ir_format_empty_ir() {
let v = X86CodeGenVerifier::new();
let test = X86CodeGenTest::new("t", X86TestCategory::IntegerOps, "int f() { return 0; }");
assert!(v.verify_ir_format("", &test)); }
#[test]
fn test_verifier_ir_format_with_expected() {
let v = X86CodeGenVerifier::new();
let mut test =
X86CodeGenTest::new("t", X86TestCategory::IntegerOps, "int f() { return 0; }");
test.expected_ir = vec!["ret i32 0".to_string()];
let ir = "define i32 @f() {\n ret i32 0\n}\n";
assert!(v.verify_ir_format(ir, &test));
}
#[test]
fn test_verifier_ir_format_missing_expected() {
let v = X86CodeGenVerifier::new();
let mut test =
X86CodeGenTest::new("t", X86TestCategory::IntegerOps, "int f() { return 0; }");
test.expected_ir = vec!["ret i32 42".to_string()];
let ir = "define i32 @f() {\n ret i32 0\n}\n";
assert!(!v.verify_ir_format(ir, &test));
}
#[test]
fn test_verifier_ir_format_unexpected() {
let v = X86CodeGenVerifier::new();
let mut test =
X86CodeGenTest::new("t", X86TestCategory::IntegerOps, "int f() { return 0; }");
test.unexpected_ir = vec!["ret i32 42".to_string()];
let ir = "define i32 @f() {\n ret i32 42\n}\n";
assert!(!v.verify_ir_format(ir, &test));
}
#[test]
fn test_verifier_asm_check() {
let v = X86CodeGenVerifier::new();
let mut test =
X86CodeGenTest::new("t", X86TestCategory::IntegerOps, "int f() { return 0; }");
test.expected_asm = vec!["ret".to_string()];
let asm = ".text\nf:\n mov $0, %eax\n ret\n";
assert!(v.verify_assembly(asm, &test));
}
#[test]
fn test_verifier_asm_missing() {
let v = X86CodeGenVerifier::new();
let mut test =
X86CodeGenTest::new("t", X86TestCategory::IntegerOps, "int f() { return 0; }");
test.expected_asm = vec!["xor %eax, %eax".to_string()];
let asm = ".text\nf:\n mov $0, %eax\n ret\n";
assert!(!v.verify_assembly(asm, &test));
}
#[test]
fn test_verifier_reset() {
let mut v = X86CodeGenVerifier::new();
v.total_checks = 10;
v.failed_checks = 3;
v.reset();
assert_eq!(v.total_checks, 0);
assert_eq!(v.failed_checks, 0);
}
#[test]
fn test_verifier_print_summary() {
let v = X86CodeGenVerifier::new();
let summary = v.print_summary();
assert!(summary.contains("passed"));
}
#[test]
fn test_verifier_all_passed() {
let v = X86CodeGenVerifier::new();
assert!(v.all_passed());
}
#[test]
fn test_verification_result_pass() {
let r = X86VerificationResult::pass("test1");
assert!(r.is_pass());
assert!(r.compiled());
}
#[test]
fn test_verification_result_fail() {
let r = X86VerificationResult::fail("test2", "assertion failed");
assert!(!r.is_pass());
assert!(!r.compiled());
assert!(r.messages[0].contains("FAIL"));
}
#[test]
fn test_generator_creation() {
let gen = X86TestGenerator::new(Some(42));
assert_eq!(gen.tests_generated, 0);
}
#[test]
fn test_generator_default() {
let gen = X86TestGenerator::default();
assert_eq!(gen.tests_generated, 0);
}
#[test]
fn test_generator_generate_test() {
let mut gen = X86TestGenerator::new(Some(12345));
let test = gen.generate_test(X86TestCategory::IntegerOps);
assert!(!test.source.is_empty());
assert!(test.name.contains("gen_"));
}
#[test]
fn test_generator_c_program_compiles() {
let mut gen = X86TestGenerator::new(Some(42));
let program = gen.generate_c_program();
assert!(!program.is_empty());
assert!(program.contains("main"));
}
#[test]
fn test_generator_edge_case_int_min() {
let mut gen = X86TestGenerator::new(Some(1));
let test = gen.generate_edge_case("int_min");
assert!(test.source.contains("INT_MIN"));
assert!(test.tags.contains(&"edge-case".to_string()));
}
#[test]
fn test_generator_edge_case_ptr_null() {
let mut gen = X86TestGenerator::new(Some(1));
let test = gen.generate_edge_case("ptr_null");
assert!(test.source.contains("(int*)0"));
}
#[test]
fn test_generator_fuzz_input() {
let mut gen = X86TestGenerator::new(Some(99));
let fuzz = gen.generate_fuzz_input();
assert!(!fuzz.is_empty());
}
#[test]
fn test_rng_state_deterministic() {
let mut rng1 = X86RngState::new(Some(42));
let mut rng2 = X86RngState::new(Some(42));
for _ in 0..100 {
assert_eq!(rng1.next_u64(), rng2.next_u64());
}
}
#[test]
fn test_rng_state_range() {
let mut rng = X86RngState::new(Some(7));
for _ in 0..100 {
let val = rng.range(5, 10);
assert!(val >= 5 && val <= 10);
}
}
#[test]
fn test_rng_pick() {
let mut rng = X86RngState::new(Some(3));
let items = vec!["a", "b", "c"];
for _ in 0..50 {
let picked = rng.pick(&items);
assert!(picked.is_some());
}
}
#[test]
fn test_rng_shuffle() {
let mut rng = X86RngState::new(Some(5));
let mut items = vec![1, 2, 3, 4, 5];
let orig = items.clone();
rng.shuffle(&mut items);
assert_eq!(items.len(), orig.len());
}
#[test]
fn test_benchmark_creation() {
let b = X86CodeGenBenchmark::new("test-bench");
assert_eq!(b.name, "test-bench");
assert!(b.enabled);
}
#[test]
fn test_benchmark_measure() {
let mut b = X86CodeGenBenchmark::new("test");
let m = b.measure("simple", "int f() { return 42; }");
assert_eq!(m.name, "simple");
assert!(m.total_time >= Duration::ZERO);
assert!(!b.measurements.is_empty());
}
#[test]
fn test_benchmark_baseline() {
let mut b = X86CodeGenBenchmark::new("test");
let m = X86BenchmarkMeasurement {
name: "baseline".into(),
binary_size: 100,
instruction_count: 10,
..Default::default()
};
b.record_baseline("test1", m);
assert!(b.baselines.contains_key("test1"));
}
#[test]
fn test_benchmark_regression_detection() {
let mut b = X86CodeGenBenchmark::new("test");
b.record_baseline(
"test1",
X86BenchmarkMeasurement {
name: "test1".into(),
binary_size: 100,
instruction_count: 10,
total_time: Duration::from_millis(100),
..Default::default()
},
);
let current = X86BenchmarkMeasurement {
name: "test1".into(),
binary_size: 200, instruction_count: 20, total_time: Duration::from_millis(200), ..Default::default()
};
let regressions = b.check_regression("test1", ¤t);
assert!(!regressions.is_empty());
}
#[test]
fn test_benchmark_print_report() {
let b = X86CodeGenBenchmark::new("test");
let report = b.print_report();
assert!(report.contains("test"));
}
#[test]
fn test_benchmark_average_time() {
let mut b = X86CodeGenBenchmark::new("test");
b.measure("t1", "int f() { return 0; }");
b.measure("t2", "int g() { return 1; }");
let _avg = b.average_time();
}
#[test]
fn test_benchmark_reset() {
let mut b = X86CodeGenBenchmark::new("test");
b.measure("t", "int f() { return 0; }");
b.reset();
assert!(b.measurements.is_empty());
}
#[test]
fn test_oracle_creation() {
let oracle = X86Oracle::new("gcc");
assert_eq!(oracle.compiler_path, "gcc");
assert_eq!(oracle.comparison_mode, OracleComparisonMode::Full);
}
#[test]
fn test_oracle_with_mode() {
let oracle = X86Oracle::new("gcc").with_mode(OracleComparisonMode::IRStructure);
assert_eq!(oracle.comparison_mode, OracleComparisonMode::IRStructure);
}
#[test]
fn test_oracle_compare_ir_structure() {
let our = "target triple = \"x86_64\"\ndefine i32 @f() { ret i32 0 }\n";
let oracle = "target triple = \"x86_64\"\ndefine i32 @f() { ret i32 0 }\n";
let diffs = X86Oracle::compare_ir_structure(our, oracle);
assert!(diffs.is_empty());
}
#[test]
fn test_oracle_compare_ir_diff_func_count() {
let our = "define i32 @f() { ret i32 0 }\ndefine i32 @g() { ret i32 1 }\n";
let oracle = "define i32 @f() { ret i32 0 }\n";
let diffs = X86Oracle::compare_ir_structure(our, oracle);
assert!(!diffs.is_empty());
}
#[test]
fn test_oracle_compare_ir_diff_keyword() {
let our = "\n";
let oracle = "target triple = \"x86_64\"\n";
let diffs = X86Oracle::compare_ir_structure(our, oracle);
assert!(!diffs.is_empty());
}
#[test]
fn test_oracle_compare_asm_behavior() {
let our = ".text\n.globl f\nf:\n mov $0, %eax\n ret\n";
let oracle = ".text\n.globl f\nf:\n xor %eax, %eax\n ret\n";
let diffs = X86Oracle::compare_assembly_behavior(our, oracle);
assert!(diffs.is_empty()); }
#[test]
fn test_oracle_compare_asm_empty() {
let our = "";
let oracle = ".text\n.globl f\nf:\n ret\n";
let diffs = X86Oracle::compare_assembly_behavior(our, oracle);
assert!(!diffs.is_empty());
}
#[test]
fn test_oracle_summary() {
let oracle = X86Oracle::new("gcc");
let summary = oracle.summary();
assert!(summary.contains("Oracle Summary"));
}
#[test]
fn test_oracle_reset() {
let mut oracle = X86Oracle::new("gcc");
oracle.results.push(OracleComparisonResult {
matches: true,
mode: OracleComparisonMode::Full,
our_output: String::new(),
oracle_output: String::new(),
differences: Vec::new(),
our_success: true,
oracle_success: true,
});
oracle.reset();
assert!(oracle.results.is_empty());
}
#[test]
fn test_integer_ops_suite() {
let suite = build_integer_ops_suite();
assert!(
suite.len() > 100,
"Expected >100 integer op tests, got {}",
suite.len()
);
}
#[test]
fn test_floating_point_suite() {
let suite = build_floating_point_suite();
assert!(
suite.len() > 20,
"Expected >20 FP tests, got {}",
suite.len()
);
}
#[test]
fn test_pointer_suite() {
let suite = build_pointer_suite();
assert!(
suite.len() > 15,
"Expected >15 pointer tests, got {}",
suite.len()
);
}
#[test]
fn test_aggregates_suite() {
let suite = build_aggregates_suite();
assert!(
suite.len() > 15,
"Expected >15 aggregate tests, got {}",
suite.len()
);
}
#[test]
fn test_control_flow_suite() {
let suite = build_control_flow_suite();
assert!(
suite.len() > 15,
"Expected >15 control flow tests, got {}",
suite.len()
);
}
#[test]
fn test_functions_suite() {
let suite = build_functions_suite();
assert!(
suite.len() > 10,
"Expected >10 function tests, got {}",
suite.len()
);
}
#[test]
fn test_cpp_features_suite() {
let suite = build_cpp_features_suite();
assert!(
suite.len() > 10,
"Expected >10 C++ tests, got {}",
suite.len()
);
}
#[test]
fn test_x86_builtins_suite() {
let suite = build_x86_builtins_suite();
assert!(
suite.len() > 10,
"Expected >10 builtin tests, got {}",
suite.len()
);
}
#[test]
fn test_abi_tests_suite() {
let suite = build_abi_tests_suite();
assert!(
suite.len() > 5,
"Expected >5 ABI tests, got {}",
suite.len()
);
}
#[test]
fn test_optimization_suite() {
let suite = build_optimization_suite();
assert!(
suite.len() > 5,
"Expected >5 optimization tests, got {}",
suite.len()
);
}
#[test]
fn test_stress_test_suite() {
let suite = build_stress_test_suite();
assert!(!suite.is_empty());
}
#[test]
fn test_all_suites_combined() {
let suites = build_all_x86_codegen_suites();
let total: usize = suites.iter().map(|s| s.len()).sum();
assert!(total >= 200, "Expected >=200 total tests, got {}", total);
}
#[test]
fn test_regression_registry_creation() {
let reg = X86RegressionRegistry::new();
assert_eq!(reg.count(), 0);
}
#[test]
fn test_regression_registry_register() {
let mut reg = X86RegressionRegistry::new();
reg.register(X86RegressionTestCase {
id: "R1".into(),
description: "test bug".into(),
source: "int f() { return 0; }".into(),
expected_behavior: "compiles".into(),
date_added: "2025-01-01".into(),
issue_ref: None,
});
assert_eq!(reg.count(), 1);
}
#[test]
fn test_build_regression_suite() {
let reg = build_regression_suite();
assert!(reg.count() > 0);
}
#[test]
fn test_runner_creation() {
let runner = X86CodeGenTestRunner::new();
assert!(!runner.stop_on_failure);
}
#[test]
fn test_runner_add_suite() {
let mut runner = X86CodeGenTestRunner::new();
let suite = X86CodeGenTestSuite::new("test", "test suite");
runner.add_suite(suite);
assert_eq!(runner.suites.len(), 1);
}
#[test]
fn test_runner_run_empty() {
let mut runner = X86CodeGenTestRunner::new();
let results = runner.run_all();
assert!(results.is_empty());
}
#[test]
fn test_suite_result_creation() {
let result = X86TestSuiteResult::new("suite1");
assert_eq!(result.suite_name, "suite1");
assert_eq!(result.total, 0);
assert!(result.is_perfect());
assert_eq!(result.pass_rate(), 100.0);
}
#[test]
fn test_suite_result_pass_rate() {
let mut result = X86TestSuiteResult::new("s");
result.total = 10;
result.passed = 8;
assert!((result.pass_rate() - 80.0).abs() < 0.01);
}
#[test]
fn test_test_category_display() {
let cats = [
X86TestCategory::IntegerOps,
X86TestCategory::FloatingPoint,
X86TestCategory::ControlFlow,
];
for cat in &cats {
let s = format!("{}", cat);
assert!(!s.is_empty());
}
}
#[test]
fn test_verification_target_display() {
let targets = [
X86VerificationTarget::IR,
X86VerificationTarget::Assembly,
X86VerificationTarget::FullPipeline,
];
for tgt in &targets {
let s = format!("{}", tgt);
assert!(!s.is_empty());
}
}
#[test]
fn test_fuzzer_creation() {
let f = X86CodeGenFuzzer::new(Some(42));
assert_eq!(f.max_iterations, 1000);
assert_eq!(f.iterations, 0);
}
#[test]
fn test_fuzzer_with_max_iterations() {
let f = X86CodeGenFuzzer::new(Some(1)).with_max_iterations(10);
assert_eq!(f.max_iterations, 10);
}
#[test]
fn test_fuzzer_print_summary() {
let f = X86CodeGenFuzzer::new(Some(1));
let summary = f.print_summary();
assert!(summary.contains("Fuzz Summary"));
}
#[test]
fn test_discover_tests_empty_dir() {
let tmp = std::env::temp_dir().join("x86_test_discover_empty");
let _ = std::fs::create_dir_all(&tmp);
let tests = discover_tests_in_directory(&tmp);
assert!(tests.is_empty());
let _ = std::fs::remove_dir_all(&tmp);
}
#[test]
fn test_full_runner_workflow() {
let mut runner = X86CodeGenTestRunner::new();
let mut suite = X86CodeGenTestSuite::new("minimal", "Minimal test suite");
suite.add(X86CodeGenTest::new(
"simple",
X86TestCategory::IntegerOps,
"int f() { return 42; }",
));
suite.add(X86CodeGenTest::new(
"add",
X86TestCategory::IntegerOps,
"int g(int a, int b) { return a + b; }",
));
runner.add_suite(suite);
let results = runner.run_all();
assert_eq!(results.len(), 1);
}
#[test]
fn test_benchmark_runner_integration() {
let mut runner = X86CodeGenTestRunner::new();
let report = runner.run_benchmarks();
assert!(!report.is_empty());
}
#[test]
fn test_compilation_verifier_basic() {
let mut verifier = X86CodeGenVerifier::new();
let test = X86CodeGenTest::new(
"basic",
X86TestCategory::IntegerOps,
"int f() { return 0; }",
);
let result = verifier.verify(&test);
assert!(!result.name.is_empty());
}
#[test]
fn test_helper_functions() {
let src = generate_many_locals_source(10);
assert!(src.contains("v0"));
assert!(src.contains("v9"));
assert!(src.contains("many_locals"));
let src = generate_deep_nesting_source(5);
assert!(src.contains("deep_nest"));
assert!(src.contains("if (x"));
let src = generate_large_switch_source(10);
assert!(src.contains("switch"));
assert!(src.contains("case 0:"));
assert!(src.contains("case 9:"));
let src = generate_call_chain_source(5);
assert!(src.contains("f0"));
assert!(src.contains("f4"));
}
#[test]
fn test_all_suites_are_non_empty() {
let suites = build_all_x86_codegen_suites();
for suite in &suites {
assert!(
!suite.is_empty(),
"Suite '{}' should not be empty",
suite.name
);
}
}
#[test]
fn test_rng_consistency() {
let mut rng = X86RngState::new(Some(0));
let val1 = rng.next_u64();
let val2 = rng.next_u64();
assert_ne!(val1, val2, "Consecutive RNG values should differ");
}
#[test]
fn test_grammar_has_required_elements() {
let g = CProgramGrammar::default();
assert!(!g.int_types.is_empty());
assert!(!g.float_types.is_empty());
assert!(!g.binary_ops.is_empty());
assert!(!g.unary_ops.is_empty());
assert!(!g.function_names.is_empty());
assert!(!g.var_names.is_empty());
}
#[test]
fn test_benchmark_measurement_default() {
let m = X86BenchmarkMeasurement::default();
assert!(m.name.is_empty());
assert_eq!(m.total_time, Duration::ZERO);
assert_eq!(m.binary_size, 0);
}
#[test]
fn test_regression_threshold_default() {
let t = X86RegressionThreshold::default();
assert!(t.time_regression_ratio > 0.0);
assert!(t.size_regression_ratio > 0.0);
}
#[test]
fn test_fuzz_status() {
assert_eq!(FuzzStatus::Pass, FuzzStatus::Pass);
assert_ne!(FuzzStatus::Pass, FuzzStatus::Timeout);
}
#[test]
fn test_oracle_comparison_result() {
let r = OracleComparisonResult {
matches: true,
mode: OracleComparisonMode::Full,
our_output: "test".into(),
oracle_output: "test".into(),
differences: vec![],
our_success: true,
oracle_success: true,
};
assert!(r.matches);
}
#[test]
fn test_print_summary_format() {
let mut runner = X86CodeGenTestRunner::new();
runner.results.push(X86TestSuiteResult::new("test"));
let summary = runner.print_summary();
assert!(summary.contains("test"));
assert!(summary.contains("TOTAL"));
}
#[test]
fn test_codegen_test_priority_bounds() {
let test = X86CodeGenTest::new("t", X86TestCategory::IntegerOps, "int f() { return 0; }")
.with_priority(10); assert_eq!(test.priority, 5);
let test = X86CodeGenTest::new("t", X86TestCategory::IntegerOps, "int f() { return 0; }")
.with_priority(0); assert_eq!(test.priority, 1);
}
#[test]
fn test_generator_produces_different_tests() {
let mut gen = X86TestGenerator::new(Some(123));
let t1 = gen.generate_c_program();
let t2 = gen.generate_c_program();
assert_ne!(t1, t2);
}
#[test]
fn test_edge_cases_exist() {
let mut gen = X86TestGenerator::new(Some(1));
let edge_types = [
"int_min",
"int_max",
"int_wrap",
"div_zero",
"ptr_null",
"large_array",
"deep_recursion",
"many_locals",
"volatile",
"const_fold",
];
for et in &edge_types {
let test = gen.generate_edge_case(et);
assert!(
!test.source.is_empty(),
"Edge case '{}' produced empty source",
et
);
}
}
#[test]
fn test_verifier_clone() {
let v1 = X86CodeGenVerifier::new();
let v2 = v1; assert!(v2.verify_ir);
}
#[test]
fn test_runner_display() {
let runner = X86CodeGenTestRunner::new();
let display = format!("{}", runner);
assert!(!display.is_empty());
}
#[test]
fn test_total_test_count() {
let suites = build_all_x86_codegen_suites();
let mut total = 0;
for suite in &suites {
total += suite.len();
}
assert!(total >= 200, "Expected >=200 tests, got {}", total);
eprintln!("Total predefined tests: {}", total);
}
#[test]
fn test_inline_asm_suite_not_empty() {
let suite = build_inline_asm_suite();
assert!(suite.len() >= 20);
}
#[test]
fn test_vector_ops_suite_not_empty() {
let suite = build_vector_ops_suite();
assert!(suite.len() >= 15);
}
#[test]
fn test_memory_ordering_suite_not_empty() {
let suite = build_memory_ordering_suite();
assert!(suite.len() >= 10);
}
#[test]
fn test_debug_info_suite_not_empty() {
let suite = build_debug_info_suite();
assert!(suite.len() >= 8);
}
#[test]
fn test_exception_handling_suite_not_empty() {
let suite = build_exception_handling_suite();
assert!(suite.len() >= 8);
}
#[test]
fn test_tls_suite_not_empty() {
let suite = build_tls_suite();
assert!(!suite.is_empty());
}
#[test]
fn test_linkage_suite_not_empty() {
let suite = build_linkage_suite();
assert!(!suite.is_empty());
}
#[test]
fn test_attributes_suite_not_empty() {
let suite = build_attributes_suite();
assert!(suite.len() >= 10);
}
#[test]
fn test_sanitizers_suite_not_empty() {
let suite = build_sanitizers_suite();
assert!(suite.len() >= 8);
}
#[test]
fn test_profiling_suite_not_empty() {
let suite = build_profiling_suite();
assert!(!suite.is_empty());
}
#[test]
fn test_lto_suite_not_empty() {
let suite = build_lto_suite();
assert!(!suite.is_empty());
}
#[test]
fn test_roundtrip_suite_not_empty() {
let suite = build_roundtrip_suite();
assert!(!suite.is_empty());
}
#[test]
fn test_differential_suite_not_empty() {
let suite = build_differential_suite();
assert!(!suite.is_empty());
}
#[test]
fn test_all_suites_extended_total() {
let suites = build_all_x86_codegen_suites_extended();
let total: usize = suites.iter().map(|s| s.len()).sum();
assert!(total >= 250);
eprintln!("Total extended tests: {}", total);
}
#[test]
fn test_delta_debugger_create() {
let dd = DeltaDebugger::new("int main() { return 0; }");
assert_eq!(dd.original, dd.current);
assert_eq!(dd.test_count, 0);
assert_eq!(dd.reduction_ratio(), 1.0);
}
#[test]
fn test_delta_debugger_stats() {
let dd = DeltaDebugger::new("hello");
let (o, c, t) = dd.stats();
assert_eq!(o, 5);
assert_eq!(c, 5);
assert_eq!(t, 0);
}
#[test]
fn test_delta_debugger_minimize_lines() {
let src = "int a;\nint b;\nint main() { return 0; }";
let mut dd = DeltaDebugger::new(src);
let r = dd.minimize_lines(|s| s.contains("main"));
assert!(r.contains("main"));
assert!(!r.contains("int a"));
}
#[test]
fn test_delta_debugger_minimize_chars() {
let src = "int main() { return 0; } // comment";
let mut dd = DeltaDebugger::new(src);
let r = dd.minimize_chars(|s| s.contains("main"));
assert!(r.contains("main"));
assert!(!r.contains("comment"));
}
#[test]
fn test_delta_debugger_preserves_interesting() {
let src = "#include <stdio.h>\nint main() { return 42; }\n";
let mut dd = DeltaDebugger::new(src);
let r = dd.minimize(|s| s.contains("return 42"));
assert!(r.contains("return 42"));
assert!(r.len() <= src.len());
}
#[test]
fn test_rng_next_i8_i16_i32_i64_no_panic() {
let mut rng = X86RngState::new(Some(1));
let _ = rng.next_i8();
let _ = rng.next_i16();
let _ = rng.next_i32();
let _ = rng.next_i64();
}
#[test]
fn test_rng_next_f32_range() {
let mut rng = X86RngState::new(Some(1));
for _ in 0..100 {
let f = rng.next_f32();
assert!(f >= 0.0 && f < 1.0);
}
}
#[test]
fn test_rng_next_f64_range() {
let mut rng = X86RngState::new(Some(1));
for _ in 0..100 {
let f = rng.next_f64();
assert!(f >= 0.0 && f < 1.0);
}
}
#[test]
fn test_rng_next_bool_covers_both() {
let mut rng = X86RngState::new(Some(42));
let mut t = false;
let mut f = false;
for _ in 0..200 {
if rng.next_bool() {
t = true;
} else {
f = true;
}
}
assert!(t && f);
}
#[test]
fn test_rng_next_bytes_length() {
let mut rng = X86RngState::new(Some(1));
assert_eq!(rng.next_bytes(32).len(), 32);
}
#[test]
fn test_rng_next_alpha_is_letter() {
let mut rng = X86RngState::new(Some(1));
for _ in 0..50 {
assert!(rng.next_alpha().is_ascii_alphabetic());
}
}
#[test]
fn test_rng_next_alnum_is_alnum() {
let mut rng = X86RngState::new(Some(1));
for _ in 0..50 {
assert!(rng.next_alnum().is_ascii_alphanumeric());
}
}
#[test]
fn test_rng_next_string_length() {
let mut rng = X86RngState::new(Some(1));
assert_eq!(rng.next_string(42).len(), 42);
}
#[test]
fn test_rng_next_c_identifier() {
let mut rng = X86RngState::new(Some(1));
for _ in 0..20 {
let id = rng.next_c_identifier();
assert!(!id.is_empty());
let first = id.chars().next().unwrap();
assert!(first.is_ascii_alphabetic() || first == '_');
}
}
#[test]
fn test_generator_generate_function_returns_valid() {
let mut gen = X86TestGenerator::new(Some(77));
let f = gen.generate_function("int", 2);
assert!(f.contains("int"));
assert!(f.contains("return"));
}
#[test]
fn test_generator_generate_struct_valid() {
let mut gen = X86TestGenerator::new(Some(77));
let s = gen.generate_struct(5);
assert!(s.contains("struct"));
assert!(s.contains(";"));
}
#[test]
fn test_generator_translation_unit_has_main() {
let mut gen = X86TestGenerator::new(Some(42));
let tu = gen.generate_translation_unit();
assert!(tu.contains("main"));
assert!(tu.contains("return"));
}
#[test]
fn test_generator_batch_count() {
let mut gen = X86TestGenerator::new(Some(99));
let batch = gen.generate_batch(10, X86TestCategory::StressTest);
assert_eq!(batch.len(), 10);
}
#[test]
fn test_benchmark_csv_format() {
let mut b = X86CodeGenBenchmark::new("csv_test");
b.measure("t", "int f() { return 0; }");
let csv = b.to_csv();
assert!(csv.contains("name,total_time_ns,binary_size"));
}
#[test]
fn test_benchmark_json_format() {
let mut b = X86CodeGenBenchmark::new("json_test");
b.measure("t", "int f() { return 0; }");
let json = b.to_json();
assert!(json.starts_with('['));
assert!(json.ends_with(']'));
assert!(json.contains("\"name\""));
}
#[test]
fn test_benchmark_fastest_returns_min() {
let mut b = X86CodeGenBenchmark::new("test");
b.measure("slow", "int f() { return 0; }");
std::thread::sleep(Duration::from_millis(2));
b.measure("fast", "int g() { return 1; }");
let fastest = b.fastest();
assert!(fastest.is_some());
}
#[test]
fn test_benchmark_median_sorted() {
let mut b = X86CodeGenBenchmark::new("test");
b.measure("a", "int f() { return 0; }");
b.measure("b", "int g() { return 1; }");
b.measure("c", "int h() { return 2; }");
let median = b.median_time();
assert!(median >= Duration::ZERO);
}
#[test]
fn test_benchmark_empty_returns_none() {
let b = X86CodeGenBenchmark::new("test");
assert!(b.fastest().is_none());
assert!(b.smallest_binary().is_none());
assert!(b.fewest_instructions().is_none());
assert_eq!(b.median_time(), Duration::ZERO);
assert_eq!(b.time_stddev(), Duration::ZERO);
}
#[test]
fn test_benchmark_smallest_binary() {
let mut b = X86CodeGenBenchmark::new("test");
b.measure("big", "int big_func(int a, int b, int c) { return a+b+c; }");
b.measure("small", "int s() { return 0; }");
assert!(b.smallest_binary().is_some());
}
#[test]
fn test_roundtrip_suite_verification_targets() {
let suite = build_roundtrip_suite();
for test in &suite.tests {
assert!(!test.verification_targets.is_empty());
}
}
#[test]
fn test_differential_suite_verification_targets() {
let suite = build_differential_suite();
for test in &suite.tests {
assert!(!test.verification_targets.is_empty());
}
}
#[test]
fn test_all_categories_have_nonempty_display() {
use X86TestCategory::*;
for cat in [
IntegerOps,
FloatingPoint,
Pointers,
Aggregates,
ControlFlow,
Functions,
CPlusPlusFeatures,
X86Builtins,
ABITests,
OptimizationTests,
DebugInfoTests,
InlineAssembly,
VectorOps,
MemoryOrdering,
ExceptionHandling,
ThreadLocalStorage,
Linkage,
Attributes,
Sanitizers,
Profiling,
LTO,
Roundtrip,
Differential,
StressTest,
FuzzTarget,
Regression,
]
.iter()
{
assert!(!format!("{}", cat).is_empty());
}
}
#[test]
fn test_generator_deterministic_with_same_seed() {
let mut g1 = X86TestGenerator::new(Some(42));
let mut g2 = X86TestGenerator::new(Some(42));
assert_eq!(g1.generate_c_program(), g2.generate_c_program());
}
#[test]
fn test_generator_different_with_different_seeds() {
let mut g1 = X86TestGenerator::new(Some(1));
let mut g2 = X86TestGenerator::new(Some(99999));
assert_ne!(g1.generate_c_program(), g2.generate_c_program());
}
#[test]
fn test_verifier_asm_empty_string() {
let v = X86CodeGenVerifier::new();
let test = X86CodeGenTest::new("t", X86TestCategory::IntegerOps, "");
assert!(v.verify_assembly("", &test));
}
#[test]
fn test_verifier_asm_x86_directives() {
let v = X86CodeGenVerifier::new();
let test = X86CodeGenTest::new("t", X86TestCategory::IntegerOps, "");
let asm = ".text\n.globl main\nmain:\n mov $0, %eax\n ret\n";
assert!(v.verify_assembly(asm, &test));
}
#[test]
fn test_lto_suite_has_flags() {
let suite = build_lto_suite();
for test in &suite.tests {
assert!(
test.flags.contains(&"-flto".to_string())
|| test.flags.contains(&"-flto=thin".to_string()),
"LTO test '{}' missing -flto flag",
test.name
);
}
}
#[test]
fn test_sanitizer_suite_has_flags() {
let suite = build_sanitizers_suite();
for test in &suite.tests {
assert!(
!test.flags.is_empty(),
"Sanitizer test '{}' has no flags",
test.name
);
}
}
}