use std::collections::{BTreeMap, HashMap, VecDeque};
use std::fmt;
use std::time::{Duration, Instant};
use crate::clang::{CLangStandard, ClangDriver, CodeModel, RelocModel};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum X86OptLevel {
O0,
O1,
O2,
O3,
Os,
Oz,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum X86OutputFormat {
PreprocessedSource,
LLVMIR,
LLVMBC,
Assembly,
Object,
SharedLib,
Executable,
}
#[derive(Debug, Clone)]
pub struct X86CompileOptions {
pub language_standard: CLangStandard,
pub target_triple: String,
pub target_cpu: String,
pub opt_level: X86OptLevel,
pub output_format: X86OutputFormat,
pub includes: Vec<String>,
pub defines: Vec<(String, Option<String>)>,
pub undefines: Vec<String>,
pub output_file: Option<String>,
pub input_files: Vec<String>,
pub wall: bool,
pub wextra: bool,
pub werror: bool,
pub pedantic: bool,
pub verbose: bool,
pub debug_info: bool,
pub pic: bool,
pub pie: bool,
pub static_linking: bool,
pub shared: bool,
pub code_model: CodeModel,
pub reloc_model: RelocModel,
pub error_limit: usize,
pub enable_cache: bool,
pub cache_dir: Option<String>,
pub print_timing: bool,
pub print_stats: bool,
}
#[derive(Debug, Clone)]
pub struct X86Pipeline {
pub options: X86CompileOptions,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum X86ClangStage {
Init,
Preprocess,
Lex,
Parse,
Sema,
IRGen,
Optimize,
CodeGen,
ISel,
RegAlloc,
FrameLower,
Encode,
Link,
Emit,
}
impl X86ClangStage {
pub fn name(&self) -> &'static str {
match self {
Self::Init => "init",
Self::Preprocess => "preprocess",
Self::Lex => "lex",
Self::Parse => "parse",
Self::Sema => "sema",
Self::IRGen => "irgen",
Self::Optimize => "optimize",
Self::CodeGen => "codegen",
Self::ISel => "isel",
Self::RegAlloc => "regalloc",
Self::FrameLower => "framelower",
Self::Encode => "encode",
Self::Link => "link",
Self::Emit => "emit",
}
}
pub fn all() -> Vec<X86ClangStage> {
vec![
Self::Init,
Self::Preprocess,
Self::Lex,
Self::Parse,
Self::Sema,
Self::IRGen,
Self::Optimize,
Self::CodeGen,
Self::ISel,
Self::RegAlloc,
Self::FrameLower,
Self::Encode,
Self::Link,
Self::Emit,
]
}
pub fn frontend_stages() -> Vec<X86ClangStage> {
vec![Self::Preprocess, Self::Lex, Self::Parse, Self::Sema]
}
pub fn middle_stages() -> Vec<X86ClangStage> {
vec![Self::IRGen, Self::Optimize]
}
pub fn backend_stages() -> Vec<X86ClangStage> {
vec![
Self::CodeGen,
Self::ISel,
Self::RegAlloc,
Self::FrameLower,
Self::Encode,
]
}
pub fn next(&self) -> Option<X86ClangStage> {
let all = Self::all();
let pos = all.iter().position(|s| s == self)?;
all.get(pos + 1).copied()
}
pub fn prev(&self) -> Option<X86ClangStage> {
let all = Self::all();
let pos = all.iter().position(|s| s == self)?;
if pos > 0 {
all.get(pos - 1).copied()
} else {
None
}
}
pub fn is_frontend(&self) -> bool {
Self::frontend_stages().contains(self)
}
pub fn is_backend(&self) -> bool {
Self::backend_stages().contains(self)
}
pub fn is_middle(&self) -> bool {
Self::middle_stages().contains(self)
}
}
impl fmt::Display for X86ClangStage {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.name())
}
}
#[derive(Debug, Clone)]
pub struct X86ClangStageResult {
pub stage: X86ClangStage,
pub success: bool,
pub duration: Duration,
pub error_count: usize,
pub warning_count: usize,
pub note_count: usize,
pub summary: String,
pub output_size: Option<usize>,
pub cache_hit: bool,
pub memory_used: Option<usize>,
}
impl X86ClangStageResult {
pub fn success(stage: X86ClangStage) -> Self {
Self {
stage,
success: true,
duration: Duration::ZERO,
error_count: 0,
warning_count: 0,
note_count: 0,
summary: format!("{} completed successfully", stage.name()),
output_size: None,
cache_hit: false,
memory_used: None,
}
}
pub fn failure(stage: X86ClangStage, reason: &str) -> Self {
Self {
stage,
success: false,
duration: Duration::ZERO,
error_count: 1,
warning_count: 0,
note_count: 0,
summary: reason.to_string(),
output_size: None,
cache_hit: false,
memory_used: None,
}
}
pub fn with_duration(mut self, d: Duration) -> Self {
self.duration = d;
self
}
pub fn with_cache_hit(mut self) -> Self {
self.cache_hit = true;
self.duration = Duration::ZERO;
self
}
pub fn with_output_size(mut self, size: usize) -> Self {
self.output_size = Some(size);
self
}
pub fn one_line(&self) -> String {
format!(
"[{:>12}] {} {} {}",
self.stage.name(),
if self.success { "✓" } else { "✗" },
if self.cache_hit { "(cached)" } else { "" },
if self.duration > Duration::ZERO {
format!("{:?}", self.duration)
} else {
String::new()
}
)
}
}
#[derive(Debug, Clone)]
pub struct X86ClangResult {
pub success: bool,
pub completed_stage: Option<X86ClangStage>,
pub failed_stage: Option<X86ClangStage>,
pub stage_results: Vec<X86ClangStageResult>,
pub total_duration: Duration,
pub total_errors: usize,
pub total_warnings: usize,
pub total_notes: usize,
pub text_output: Option<String>,
pub binary_output: Option<Vec<u8>>,
pub output_file: Option<String>,
pub cache_hits: usize,
pub cache_misses: usize,
pub source_hash: String,
}
impl X86ClangResult {
pub fn new() -> Self {
Self {
success: true,
completed_stage: None,
failed_stage: None,
stage_results: Vec::new(),
total_duration: Duration::ZERO,
total_errors: 0,
total_warnings: 0,
total_notes: 0,
text_output: None,
binary_output: None,
output_file: None,
cache_hits: 0,
cache_misses: 0,
source_hash: String::new(),
}
}
pub fn success_with(stage_results: Vec<X86ClangStageResult>, output: String) -> Self {
let total_duration: Duration = stage_results.iter().map(|r| r.duration).sum();
let total_errors: usize = stage_results.iter().map(|r| r.error_count).sum();
let total_warnings: usize = stage_results.iter().map(|r| r.warning_count).sum();
let total_notes: usize = stage_results.iter().map(|r| r.note_count).sum();
let cache_hits: usize = stage_results.iter().filter(|r| r.cache_hit).count();
let cache_misses: usize = stage_results.iter().filter(|r| !r.cache_hit).count();
let last = stage_results.last().map(|r| r.stage);
Self {
success: true,
completed_stage: last,
failed_stage: None,
stage_results,
total_duration,
total_errors,
total_warnings,
total_notes,
text_output: Some(output),
binary_output: None,
output_file: None,
cache_hits,
cache_misses,
source_hash: String::new(),
}
}
pub fn failure(stage: X86ClangStage, reason: &str) -> Self {
let sr = X86ClangStageResult::failure(stage, reason);
Self {
success: false,
completed_stage: None,
failed_stage: Some(stage),
stage_results: vec![sr],
total_duration: Duration::ZERO,
total_errors: 1,
total_warnings: 0,
total_notes: 0,
text_output: None,
binary_output: None,
output_file: None,
cache_hits: 0,
cache_misses: 0,
source_hash: String::new(),
}
}
pub fn format_report(&self) -> String {
let mut out = String::new();
out.push_str("╔══════════════════════════════════════════════╗\n");
out.push_str("║ X86 Clang Pipeline Compilation ║\n");
out.push_str("╠══════════════════════════════════════════════╣\n");
out.push_str(&format!(
"║ Status: {:>36} ║\n",
if self.success { "SUCCESS" } else { "FAILURE" }
));
if let Some(s) = self.failed_stage {
out.push_str(&format!("║ Failed at: {:>32} ║\n", s.name()));
}
out.push_str("╠══════════════════════════════════════════════╣\n");
for sr in &self.stage_results {
out.push_str(&format!("║ {}\n", sr.one_line()));
out.push_str(&format!(
"║ errors={}, warnings={}, notes={}\n",
sr.error_count, sr.warning_count, sr.note_count
));
}
out.push_str("╠══════════════════════════════════════════════╣\n");
out.push_str(&format!(
"║ Total: errors={}, warnings={}, notes={}\n",
self.total_errors, self.total_warnings, self.total_notes
));
out.push_str(&format!(
"║ Cache: {} hits, {} misses\n",
self.cache_hits, self.cache_misses
));
out.push_str(&format!("║ Duration: {:?}\n", self.total_duration));
out.push_str("╚══════════════════════════════════════════════╝\n");
out
}
}
impl Default for X86ClangResult {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct X86ClangCompileOptions {
pub standard: CLangStandard,
pub target_triple: String,
pub target_cpu: String,
pub opt_level: X86OptLevel,
pub output_format: X86OutputFormat,
pub include_paths: Vec<String>,
pub defines: Vec<(String, Option<String>)>,
pub undefines: Vec<String>,
pub output_file: Option<String>,
pub input_files: Vec<String>,
pub wall: bool,
pub wextra: bool,
pub werror: bool,
pub pedantic: bool,
pub verbose: bool,
pub debug_info: bool,
pub pic: bool,
pub pie: bool,
pub static_linking: bool,
pub shared: bool,
pub code_model: CodeModel,
pub reloc_model: RelocModel,
pub stop_after: Option<X86ClangStage>,
pub error_limit: usize,
pub enable_cache: bool,
pub cache_dir: Option<String>,
pub max_jobs: usize,
pub incremental: bool,
pub time_trace: bool,
pub print_stats: bool,
}
impl X86ClangCompileOptions {
pub fn x86_64_linux() -> Self {
Self {
standard: CLangStandard::C17,
target_triple: "x86_64-unknown-linux-gnu".into(),
target_cpu: "x86-64".into(),
opt_level: X86OptLevel::O2,
output_format: X86OutputFormat::Object,
include_paths: Vec::new(),
defines: Vec::new(),
undefines: Vec::new(),
output_file: None,
input_files: Vec::new(),
wall: true,
wextra: false,
werror: false,
pedantic: false,
verbose: false,
debug_info: false,
pic: false,
pie: false,
static_linking: false,
shared: false,
code_model: CodeModel::Default,
reloc_model: RelocModel::Default,
stop_after: None,
error_limit: 20,
enable_cache: false,
cache_dir: None,
max_jobs: 1,
incremental: false,
time_trace: false,
print_stats: false,
}
}
pub fn x86_64_linux_release() -> Self {
Self {
opt_level: X86OptLevel::O3,
..Self::x86_64_linux()
}
}
pub fn x86_32_linux() -> Self {
Self {
target_triple: "i386-unknown-linux-gnu".into(),
target_cpu: "i386".into(),
..Self::x86_64_linux()
}
}
pub fn from_x86_compile_options(opts: &X86CompileOptions) -> Self {
Self {
standard: opts.language_standard,
target_triple: opts.target_triple.clone(),
target_cpu: opts.target_cpu.clone(),
opt_level: opts.opt_level,
output_format: opts.output_format,
include_paths: opts.includes.clone(),
defines: opts.defines.clone(),
undefines: opts.undefines.clone(),
output_file: opts.output_file.clone(),
input_files: opts.input_files.clone(),
wall: opts.wall,
wextra: opts.wextra,
werror: opts.werror,
pedantic: opts.pedantic,
verbose: opts.verbose,
debug_info: opts.debug_info,
pic: opts.pic,
pie: opts.pie,
static_linking: opts.static_linking,
shared: opts.shared,
code_model: opts.code_model,
reloc_model: opts.reloc_model,
stop_after: None,
error_limit: opts.error_limit,
enable_cache: opts.enable_cache,
cache_dir: opts.cache_dir.clone(),
max_jobs: 1,
incremental: false,
time_trace: opts.print_timing,
print_stats: opts.print_stats,
}
}
}
impl Default for X86ClangCompileOptions {
fn default() -> Self {
Self::x86_64_linux()
}
}
#[derive(Debug, Clone)]
pub struct X86IncrementalTracker {
pub file_hashes: HashMap<String, String>,
pub changed_files: Vec<String>,
pub removed_files: Vec<String>,
pub added_files: Vec<String>,
pub modified_symbols: Vec<String>,
pub recompile_all: bool,
}
impl X86IncrementalTracker {
pub fn new() -> Self {
Self {
file_hashes: HashMap::new(),
changed_files: Vec::new(),
removed_files: Vec::new(),
added_files: Vec::new(),
modified_symbols: Vec::new(),
recompile_all: false,
}
}
pub fn detect_changes(&mut self, files: &[String], get_hash: impl Fn(&str) -> Option<String>) {
self.changed_files.clear();
self.removed_files.clear();
self.added_files.clear();
let old_files: Vec<String> = self.file_hashes.keys().cloned().collect();
for file in files {
if let Some(hash) = get_hash(file) {
match self.file_hashes.get(file) {
Some(old) if old == &hash => { }
Some(_) => {
self.changed_files.push(file.clone());
self.file_hashes.insert(file.clone(), hash);
}
None => {
self.added_files.push(file.clone());
self.file_hashes.insert(file.clone(), hash);
}
}
}
}
for old_file in &old_files {
if !files.contains(old_file) {
self.removed_files.push(old_file.clone());
self.file_hashes.remove(old_file);
}
}
}
pub fn has_changes(&self) -> bool {
!self.changed_files.is_empty()
|| !self.added_files.is_empty()
|| !self.removed_files.is_empty()
}
pub fn needs_full_recompile(&self) -> bool {
self.recompile_all || !self.removed_files.is_empty()
}
pub fn affected_units(&self) -> Vec<String> {
let mut units = self.changed_files.clone();
units.extend(self.added_files.clone());
units.sort();
units.dedup();
units
}
pub fn clear(&mut self) {
self.changed_files.clear();
self.removed_files.clear();
self.added_files.clear();
self.modified_symbols.clear();
self.recompile_all = false;
}
}
impl Default for X86IncrementalTracker {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum X86ClangDiagLevel {
Note,
Warning,
Error,
Fatal,
}
impl fmt::Display for X86ClangDiagLevel {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Note => write!(f, "note"),
Self::Warning => write!(f, "warning"),
Self::Error => write!(f, "error"),
Self::Fatal => write!(f, "fatal"),
}
}
}
#[derive(Debug, Clone)]
pub struct X86ClangDiagnostic {
pub level: X86ClangDiagLevel,
pub message: String,
pub file: Option<String>,
pub line: usize,
pub column: usize,
pub stage: X86ClangStage,
pub code: Option<String>,
pub fixit: Option<String>,
}
impl fmt::Display for X86ClangDiagnostic {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(ref file) = self.file {
write!(f, "{}:{}:{}: ", file, self.line, self.column)?;
}
write!(f, "{} ", self.level)?;
if let Some(ref code) = self.code {
write!(f, "[{}] ", code)?;
}
write!(f, "{}", self.message)?;
if let Some(ref fixit) = self.fixit {
write!(f, " (fix: {})", fixit)?;
}
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct X86ClangDiagnosticEngine {
pub diagnostics: Vec<X86ClangDiagnostic>,
pub error_limit: usize,
pub werror: bool,
pub stage_filters: Vec<X86ClangStage>,
pub suppress_notes: bool,
}
impl X86ClangDiagnosticEngine {
pub fn new() -> Self {
Self {
diagnostics: Vec::new(),
error_limit: 20,
werror: false,
stage_filters: Vec::new(),
suppress_notes: false,
}
}
pub fn error(&mut self, stage: X86ClangStage, msg: &str) {
self.emit(X86ClangDiagLevel::Error, stage, msg, None, None);
}
pub fn warning(&mut self, stage: X86ClangStage, msg: &str) {
self.emit(X86ClangDiagLevel::Warning, stage, msg, None, None);
}
pub fn note(&mut self, stage: X86ClangStage, msg: &str) {
if self.suppress_notes {
return;
}
self.emit(X86ClangDiagLevel::Note, stage, msg, None, None);
}
pub fn fatal(&mut self, stage: X86ClangStage, msg: &str) {
self.emit(X86ClangDiagLevel::Fatal, stage, msg, None, None);
}
fn emit(
&mut self,
level: X86ClangDiagLevel,
stage: X86ClangStage,
msg: &str,
code: Option<&str>,
fixit: Option<&str>,
) {
if matches!(level, X86ClangDiagLevel::Error | X86ClangDiagLevel::Fatal) {
if self.error_count() >= self.error_limit {
return;
}
}
if !self.stage_filters.is_empty() && !self.stage_filters.contains(&stage) {
return;
}
self.diagnostics.push(X86ClangDiagnostic {
level,
message: msg.to_string(),
file: None,
line: 0,
column: 0,
stage,
code: code.map(|s| s.to_string()),
fixit: fixit.map(|s| s.to_string()),
});
}
pub fn error_count(&self) -> usize {
self.diagnostics
.iter()
.filter(|d| matches!(d.level, X86ClangDiagLevel::Error | X86ClangDiagLevel::Fatal))
.count()
}
pub fn warning_count(&self) -> usize {
self.diagnostics
.iter()
.filter(|d| matches!(d.level, X86ClangDiagLevel::Warning))
.count()
+ if self.werror { self.error_count() } else { 0 }
}
pub fn note_count(&self) -> usize {
self.diagnostics
.iter()
.filter(|d| matches!(d.level, X86ClangDiagLevel::Note))
.count()
}
pub fn has_errors(&self) -> bool {
self.error_count() > 0
}
pub fn has_fatal(&self) -> bool {
self.diagnostics
.iter()
.any(|d| matches!(d.level, X86ClangDiagLevel::Fatal))
}
pub fn clear(&mut self) {
self.diagnostics.clear();
}
pub fn drain(&mut self) -> Vec<X86ClangDiagnostic> {
std::mem::take(&mut self.diagnostics)
}
pub fn format_all(&self) -> String {
let mut out = String::new();
for diag in &self.diagnostics {
out.push_str(&diag.to_string());
out.push('\n');
}
out
}
pub fn has_no_errors(&self) -> bool {
!self.has_errors()
}
}
impl Default for X86ClangDiagnosticEngine {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct X86ClangStats {
pub total_compilations: usize,
pub successful_compilations: usize,
pub failed_compilations: usize,
pub stage_durations: BTreeMap<X86ClangStage, Duration>,
pub stage_counts: BTreeMap<X86ClangStage, usize>,
pub stage_errors: BTreeMap<X86ClangStage, usize>,
pub cache_hits: usize,
pub cache_misses: usize,
pub total_lines_processed: usize,
pub total_tokens: usize,
pub total_functions: usize,
pub peak_memory_bytes: Option<usize>,
pub start_time: Option<Instant>,
}
impl X86ClangStats {
pub fn new() -> Self {
Self {
total_compilations: 0,
successful_compilations: 0,
failed_compilations: 0,
stage_durations: BTreeMap::new(),
stage_counts: BTreeMap::new(),
stage_errors: BTreeMap::new(),
cache_hits: 0,
cache_misses: 0,
total_lines_processed: 0,
total_tokens: 0,
total_functions: 0,
peak_memory_bytes: None,
start_time: None,
}
}
pub fn record_stage(&mut self, result: &X86ClangStageResult) {
*self.stage_durations.entry(result.stage).or_default() += result.duration;
*self.stage_counts.entry(result.stage).or_default() += 1;
*self.stage_errors.entry(result.stage).or_default() += result.error_count;
if result.cache_hit {
self.cache_hits += 1;
} else {
self.cache_misses += 1;
}
}
pub fn record_success(&mut self) {
self.total_compilations += 1;
self.successful_compilations += 1;
}
pub fn record_failure(&mut self) {
self.total_compilations += 1;
self.failed_compilations += 1;
}
pub fn cache_hit_rate(&self) -> f64 {
let total = self.cache_hits + self.cache_misses;
if total == 0 {
return 0.0;
}
self.cache_hits as f64 / total as f64
}
pub fn format_report(&self) -> String {
let mut out = String::new();
out.push_str("╔══════════════════════════════════════════════╗\n");
out.push_str("║ Pipeline Statistics Report ║\n");
out.push_str("╠══════════════════════════════════════════════╣\n");
out.push_str(&format!(
"║ Compilations: {} total, {} ok, {} failed\n",
self.total_compilations, self.successful_compilations, self.failed_compilations
));
out.push_str(&format!(
"║ Cache: {:.1}% hit rate ({} hits, {} misses)\n",
self.cache_hit_rate() * 100.0,
self.cache_hits,
self.cache_misses
));
out.push_str("╠══════════════════════════════════════════════╣\n");
out.push_str("║ Stage Count Errors Avg Time\n");
for stage in X86ClangStage::all() {
let count = self.stage_counts.get(&stage).copied().unwrap_or(0);
let errors = self.stage_errors.get(&stage).copied().unwrap_or(0);
let total_dur = self
.stage_durations
.get(&stage)
.copied()
.unwrap_or_default();
let avg = if count > 0 {
format!("{:?}", total_dur / count as u32)
} else {
"—".to_string()
};
out.push_str(&format!(
"║ {:12} {:5} {:7} {}\n",
stage.name(),
count,
errors,
avg
));
}
out.push_str("╚══════════════════════════════════════════════╝\n");
out
}
}
impl Default for X86ClangStats {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct X86ClangPipeline {
pub options: X86ClangCompileOptions,
pub diagnostics: X86ClangDiagnosticEngine,
pub stats: X86ClangStats,
pub stage_results: Vec<X86ClangStageResult>,
pub incremental_tracker: X86IncrementalTracker,
pub current_source: Option<String>,
pub current_stage: Option<X86ClangStage>,
pipeline_start: Instant,
}
impl X86ClangPipeline {
pub fn new(options: X86ClangCompileOptions) -> Self {
Self {
options,
diagnostics: X86ClangDiagnosticEngine::new(),
stats: X86ClangStats::new(),
stage_results: Vec::new(),
incremental_tracker: X86IncrementalTracker::new(),
current_source: None,
current_stage: None,
pipeline_start: Instant::now(),
}
}
pub fn from_x86_compile_options(opts: X86CompileOptions) -> Self {
let clang_opts = X86ClangCompileOptions::from_x86_compile_options(&opts);
Self::new(clang_opts)
}
pub fn run(&mut self, source: &str) -> X86ClangResult {
self.pipeline_start = Instant::now();
self.current_source = Some(source.to_string());
self.stage_results.clear();
self.diagnostics.clear();
let mut result = X86ClangResult::new();
let init_result = self.run_init();
if !init_result.success {
result.stage_results.push(init_result);
return X86ClangResult::failure(X86ClangStage::Init, "initialization failed");
}
result.stage_results.push(init_result);
if self.should_stop_after(X86ClangStage::Preprocess) {
let r = self.run_preprocess(source);
result.stage_results.push(r);
return self.finalize_result(result);
}
let pre = self.run_preprocess(source);
result.stage_results.push(pre.clone());
if !pre.success {
return self.finalize_failure(result, X86ClangStage::Preprocess);
}
if self.should_stop_after(X86ClangStage::Lex) {
let r = self.run_lex(source);
result.stage_results.push(r);
return self.finalize_result(result);
}
let lex = self.run_lex(source);
result.stage_results.push(lex.clone());
if !lex.success {
return self.finalize_failure(result, X86ClangStage::Lex);
}
if self.should_stop_after(X86ClangStage::Parse) {
let r = self.run_parse(source);
result.stage_results.push(r);
return self.finalize_result(result);
}
let parse = self.run_parse(source);
result.stage_results.push(parse.clone());
if !parse.success {
return self.finalize_failure(result, X86ClangStage::Parse);
}
if self.should_stop_after(X86ClangStage::Sema) {
let r = self.run_sema(source);
result.stage_results.push(r);
return self.finalize_result(result);
}
let sema = self.run_sema(source);
result.stage_results.push(sema.clone());
if !sema.success {
return self.finalize_failure(result, X86ClangStage::Sema);
}
if self.should_stop_after(X86ClangStage::IRGen) {
let r = self.run_irgen(source);
result.stage_results.push(r);
return self.finalize_result(result);
}
let irgen = self.run_irgen(source);
result.stage_results.push(irgen.clone());
if !irgen.success {
return self.finalize_failure(result, X86ClangStage::IRGen);
}
if self.should_stop_after(X86ClangStage::Optimize) {
let r = self.run_optimize();
result.stage_results.push(r);
return self.finalize_result(result);
}
let opt = self.run_optimize();
result.stage_results.push(opt.clone());
if !opt.success {
return self.finalize_failure(result, X86ClangStage::Optimize);
}
if self.should_stop_after(X86ClangStage::CodeGen) {
let r = self.run_codegen();
result.stage_results.push(r);
return self.finalize_result(result);
}
let cg = self.run_codegen();
result.stage_results.push(cg.clone());
if !cg.success {
return self.finalize_failure(result, X86ClangStage::CodeGen);
}
let isel = self.run_isel();
result.stage_results.push(isel.clone());
if !isel.success {
return self.finalize_failure(result, X86ClangStage::ISel);
}
let ra = self.run_regalloc();
result.stage_results.push(ra.clone());
if !ra.success {
return self.finalize_failure(result, X86ClangStage::RegAlloc);
}
let fl = self.run_framelower();
result.stage_results.push(fl.clone());
if !fl.success {
return self.finalize_failure(result, X86ClangStage::FrameLower);
}
let enc = self.run_encode();
result.stage_results.push(enc.clone());
if !enc.success {
return self.finalize_failure(result, X86ClangStage::Encode);
}
self.finalize_result(result)
}
fn should_stop_after(&self, stage: X86ClangStage) -> bool {
self.options.stop_after == Some(stage)
}
fn finalize_result(&self, mut result: X86ClangResult) -> X86ClangResult {
let total_duration: Duration = result.stage_results.iter().map(|r| r.duration).sum();
let total_errors: usize = result.stage_results.iter().map(|r| r.error_count).sum();
let total_warnings: usize = result.stage_results.iter().map(|r| r.warning_count).sum();
let total_notes: usize = result.stage_results.iter().map(|r| r.note_count).sum();
let cache_hits: usize = result.stage_results.iter().filter(|r| r.cache_hit).count();
let cache_misses: usize = result.stage_results.iter().filter(|r| !r.cache_hit).count();
let last = result.stage_results.last().map(|r| r.stage);
result.total_duration = total_duration;
result.total_errors = total_errors;
result.total_warnings = total_warnings;
result.total_notes = total_notes;
result.cache_hits = cache_hits;
result.cache_misses = cache_misses;
result.completed_stage = last;
result.success = total_errors == 0 && !self.diagnostics.has_fatal();
result.text_output = self.current_source.clone();
result
}
fn finalize_failure(&self, mut result: X86ClangResult, stage: X86ClangStage) -> X86ClangResult {
result.success = false;
result.failed_stage = Some(stage);
result.completed_stage = stage.prev();
result
}
fn run_init(&mut self) -> X86ClangStageResult {
self.current_stage = Some(X86ClangStage::Init);
X86ClangStageResult::success(X86ClangStage::Init)
}
fn run_preprocess(&mut self, _source: &str) -> X86ClangStageResult {
self.current_stage = Some(X86ClangStage::Preprocess);
let start = Instant::now();
let result = X86ClangStageResult::success(X86ClangStage::Preprocess)
.with_duration(start.elapsed())
.with_output_size(_source.len());
self.stats.record_stage(&result);
result
}
fn run_lex(&mut self, source: &str) -> X86ClangStageResult {
self.current_stage = Some(X86ClangStage::Lex);
let start = Instant::now();
let token_count = source.split_whitespace().count();
let mut result =
X86ClangStageResult::success(X86ClangStage::Lex).with_duration(start.elapsed());
if token_count == 0 && !source.is_empty() {
self.diagnostics
.warning(X86ClangStage::Lex, "empty token stream after preprocessing");
result.warning_count = 1;
}
self.stats.record_stage(&result);
result
}
fn run_parse(&mut self, source: &str) -> X86ClangStageResult {
self.current_stage = Some(X86ClangStage::Parse);
let start = Instant::now();
let result = match crate::clang::driver::compile_c_string(source) {
Ok(_) => {
X86ClangStageResult::success(X86ClangStage::Parse).with_duration(start.elapsed())
}
Err(e) => {
let err_msg = e.join("; ");
self.diagnostics.error(X86ClangStage::Parse, &err_msg);
let mut r = X86ClangStageResult::failure(X86ClangStage::Parse, &err_msg);
r.duration = start.elapsed();
r
}
};
self.stats.record_stage(&result);
result
}
fn run_sema(&mut self, _source: &str) -> X86ClangStageResult {
self.current_stage = Some(X86ClangStage::Sema);
let start = Instant::now();
let result =
X86ClangStageResult::success(X86ClangStage::Sema).with_duration(start.elapsed());
self.stats.record_stage(&result);
result
}
fn run_irgen(&mut self, source: &str) -> X86ClangStageResult {
self.current_stage = Some(X86ClangStage::IRGen);
let start = Instant::now();
let result = X86ClangStageResult::success(X86ClangStage::IRGen)
.with_duration(start.elapsed())
.with_output_size(source.len() * 4); self.stats.record_stage(&result);
result
}
fn run_optimize(&mut self) -> X86ClangStageResult {
self.current_stage = Some(X86ClangStage::Optimize);
let start = Instant::now();
let result = if self.options.opt_level == X86OptLevel::O0 {
X86ClangStageResult::success(X86ClangStage::Optimize).with_duration(Duration::ZERO)
} else {
X86ClangStageResult::success(X86ClangStage::Optimize).with_duration(start.elapsed())
};
self.stats.record_stage(&result);
result
}
fn run_codegen(&mut self) -> X86ClangStageResult {
self.current_stage = Some(X86ClangStage::CodeGen);
let start = Instant::now();
let result =
X86ClangStageResult::success(X86ClangStage::CodeGen).with_duration(start.elapsed());
self.stats.record_stage(&result);
result
}
fn run_isel(&mut self) -> X86ClangStageResult {
self.current_stage = Some(X86ClangStage::ISel);
let start = Instant::now();
let result =
X86ClangStageResult::success(X86ClangStage::ISel).with_duration(start.elapsed());
self.stats.record_stage(&result);
result
}
fn run_regalloc(&mut self) -> X86ClangStageResult {
self.current_stage = Some(X86ClangStage::RegAlloc);
let start = Instant::now();
let result =
X86ClangStageResult::success(X86ClangStage::RegAlloc).with_duration(start.elapsed());
self.stats.record_stage(&result);
result
}
fn run_framelower(&mut self) -> X86ClangStageResult {
self.current_stage = Some(X86ClangStage::FrameLower);
let start = Instant::now();
let result =
X86ClangStageResult::success(X86ClangStage::FrameLower).with_duration(start.elapsed());
self.stats.record_stage(&result);
result
}
fn run_encode(&mut self) -> X86ClangStageResult {
self.current_stage = Some(X86ClangStage::Encode);
let start = Instant::now();
let result = X86ClangStageResult::success(X86ClangStage::Encode)
.with_duration(start.elapsed())
.with_output_size(1024); self.stats.record_stage(&result);
result
}
pub fn report(&self) -> String {
let mut out = String::new();
out.push_str("X86ClangPipeline Report\n");
out.push_str("=======================\n");
out.push_str(&format!("Target: {}\n", self.options.target_triple));
out.push_str(&format!("Standard: {:?}\n", self.options.standard));
out.push_str(&format!("Opt level: {:?}\n", self.options.opt_level));
out.push_str(&format!(
"Output format: {:?}\n",
self.options.output_format
));
out.push_str(&format!(
"Diagnostics: {} errors, {} warnings\n",
self.diagnostics.error_count(),
self.diagnostics.warning_count()
));
out.push_str(&format!("Stages completed: {}\n", self.stage_results.len()));
for sr in &self.stage_results {
out.push_str(&format!(" {}\n", sr.one_line()));
}
out
}
pub fn stats_report(&self) -> String {
self.stats.format_report()
}
}
pub fn x86_clang_compile(source: &str) -> X86ClangResult {
let opts = X86ClangCompileOptions::x86_64_linux();
let mut pipeline = X86ClangPipeline::new(opts);
pipeline.run(source)
}
pub fn x86_clang_compile_with_opts(source: &str, opts: X86ClangCompileOptions) -> X86ClangResult {
let mut pipeline = X86ClangPipeline::new(opts);
pipeline.run(source)
}
pub fn x86_clang_compile_stop_after(source: &str, stage: X86ClangStage) -> X86ClangResult {
let mut opts = X86ClangCompileOptions::x86_64_linux();
opts.stop_after = Some(stage);
let mut pipeline = X86ClangPipeline::new(opts);
pipeline.run(source)
}
pub fn make_x86_release_pipeline() -> X86ClangPipeline {
X86ClangPipeline::new(X86ClangCompileOptions::x86_64_linux_release())
}
pub fn make_x86_32_pipeline() -> X86ClangPipeline {
X86ClangPipeline::new(X86ClangCompileOptions::x86_32_linux())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_x86_clang_stage_names() {
assert_eq!(X86ClangStage::Init.name(), "init");
assert_eq!(X86ClangStage::Preprocess.name(), "preprocess");
assert_eq!(X86ClangStage::Lex.name(), "lex");
assert_eq!(X86ClangStage::Parse.name(), "parse");
assert_eq!(X86ClangStage::Sema.name(), "sema");
assert_eq!(X86ClangStage::IRGen.name(), "irgen");
assert_eq!(X86ClangStage::Optimize.name(), "optimize");
assert_eq!(X86ClangStage::CodeGen.name(), "codegen");
assert_eq!(X86ClangStage::ISel.name(), "isel");
assert_eq!(X86ClangStage::RegAlloc.name(), "regalloc");
assert_eq!(X86ClangStage::FrameLower.name(), "framelower");
assert_eq!(X86ClangStage::Encode.name(), "encode");
assert_eq!(X86ClangStage::Link.name(), "link");
assert_eq!(X86ClangStage::Emit.name(), "emit");
}
#[test]
fn test_x86_clang_stage_order() {
let stages = X86ClangStage::all();
for i in 1..stages.len() {
assert!(stages[i - 1] < stages[i]);
}
}
#[test]
fn test_x86_clang_stage_next_prev() {
assert_eq!(X86ClangStage::Init.next(), Some(X86ClangStage::Preprocess));
assert_eq!(X86ClangStage::Preprocess.prev(), Some(X86ClangStage::Init));
assert_eq!(X86ClangStage::Emit.next(), None);
assert_eq!(X86ClangStage::Init.prev(), None);
}
#[test]
fn test_x86_clang_stage_is_frontend() {
assert!(X86ClangStage::Preprocess.is_frontend());
assert!(X86ClangStage::Lex.is_frontend());
assert!(X86ClangStage::Parse.is_frontend());
assert!(!X86ClangStage::CodeGen.is_frontend());
}
#[test]
fn test_x86_clang_stage_is_backend() {
assert!(X86ClangStage::ISel.is_backend());
assert!(X86ClangStage::Encode.is_backend());
assert!(!X86ClangStage::Parse.is_backend());
}
#[test]
fn test_x86_clang_stage_display() {
assert_eq!(X86ClangStage::Parse.to_string(), "parse");
assert_eq!(X86ClangStage::Optimize.to_string(), "optimize");
}
#[test]
fn test_stage_result_success() {
let result = X86ClangStageResult::success(X86ClangStage::Parse);
assert!(result.success);
assert_eq!(result.error_count, 0);
}
#[test]
fn test_stage_result_failure() {
let result = X86ClangStageResult::failure(X86ClangStage::Sema, "type mismatch");
assert!(!result.success);
assert_eq!(result.error_count, 1);
assert!(result.summary.contains("type mismatch"));
}
#[test]
fn test_stage_result_with_duration() {
let result = X86ClangStageResult::success(X86ClangStage::Lex)
.with_duration(Duration::from_millis(5));
assert_eq!(result.duration, Duration::from_millis(5));
}
#[test]
fn test_stage_result_with_cache_hit() {
let result = X86ClangStageResult::success(X86ClangStage::Parse).with_cache_hit();
assert!(result.cache_hit);
}
#[test]
fn test_stage_result_with_output_size() {
let result = X86ClangStageResult::success(X86ClangStage::Encode).with_output_size(4096);
assert_eq!(result.output_size, Some(4096));
}
#[test]
fn test_stage_result_one_line() {
let result = X86ClangStageResult::success(X86ClangStage::Parse);
let line = result.one_line();
assert!(line.contains("parse"));
assert!(line.contains("✓"));
}
#[test]
fn test_x86_clang_compile_options_default() {
let opts = X86ClangCompileOptions::default();
assert!(opts.target_triple.contains("x86_64"));
assert!(opts.wall);
assert!(!opts.werror);
}
#[test]
fn test_x86_clang_compile_options_x86_64_linux() {
let opts = X86ClangCompileOptions::x86_64_linux();
assert_eq!(opts.opt_level, X86OptLevel::O2);
assert_eq!(opts.target_triple, "x86_64-unknown-linux-gnu");
}
#[test]
fn test_x86_clang_compile_options_release() {
let opts = X86ClangCompileOptions::x86_64_linux_release();
assert_eq!(opts.opt_level, X86OptLevel::O3);
}
#[test]
fn test_x86_clang_compile_options_32() {
let opts = X86ClangCompileOptions::x86_32_linux();
assert!(opts.target_triple.contains("i386"));
}
#[test]
fn test_x86_clang_compile_options_from_x86() {
let x86_opts = X86CompileOptions::x86_64_linux();
let clang_opts = X86ClangCompileOptions::from_x86_compile_options(&x86_opts);
assert_eq!(clang_opts.target_triple, x86_opts.target_triple);
}
#[test]
fn test_x86_clang_result_default() {
let result = X86ClangResult::default();
assert!(result.success);
assert_eq!(result.total_errors, 0);
}
#[test]
fn test_x86_clang_result_failure() {
let result = X86ClangResult::failure(X86ClangStage::Parse, "syntax error");
assert!(!result.success);
assert_eq!(result.failed_stage, Some(X86ClangStage::Parse));
}
#[test]
fn test_x86_clang_result_format_report() {
let result = X86ClangResult::failure(X86ClangStage::Sema, "test error");
let report = result.format_report();
assert!(report.contains("FAILURE"));
assert!(report.contains("sema"));
}
#[test]
fn test_x86_clang_result_success_with() {
let sr = vec![
X86ClangStageResult::success(X86ClangStage::Init),
X86ClangStageResult::success(X86ClangStage::Parse),
];
let result = X86ClangResult::success_with(sr, "output".into());
assert!(result.success);
assert_eq!(result.stage_results.len(), 2);
assert_eq!(result.text_output, Some("output".into()));
}
#[test]
fn test_incremental_tracker_creation() {
let tracker = X86IncrementalTracker::new();
assert!(!tracker.has_changes());
assert!(!tracker.needs_full_recompile());
}
#[test]
fn test_incremental_tracker_detect_changes() {
let mut tracker = X86IncrementalTracker::new();
let files = vec!["a.c".into(), "b.c".into()];
tracker.detect_changes(&files, |f| Some(format!("hash_{}", f)));
assert!(tracker.has_changes());
assert_eq!(tracker.added_files.len(), 2);
}
#[test]
fn test_incremental_tracker_no_changes() {
let mut tracker = X86IncrementalTracker::new();
tracker.file_hashes.insert("a.c".into(), "hash1".into());
let files = vec!["a.c".into()];
tracker.detect_changes(&files, |_| Some("hash1".into()));
assert!(!tracker.has_changes());
}
#[test]
fn test_incremental_tracker_clear() {
let mut tracker = X86IncrementalTracker::new();
tracker.changed_files.push("a.c".into());
tracker.clear();
assert!(tracker.changed_files.is_empty());
}
#[test]
fn test_incremental_tracker_affected_units() {
let mut tracker = X86IncrementalTracker::new();
tracker.changed_files.push("a.c".into());
tracker.added_files.push("b.c".into());
let affected = tracker.affected_units();
assert!(affected.contains(&"a.c".to_string()));
assert!(affected.contains(&"b.c".to_string()));
}
#[test]
fn test_diag_engine_creation() {
let engine = X86ClangDiagnosticEngine::new();
assert!(!engine.has_errors());
assert_eq!(engine.error_count(), 0);
}
#[test]
fn test_diag_engine_error() {
let mut engine = X86ClangDiagnosticEngine::new();
engine.error(X86ClangStage::Parse, "syntax error");
assert!(engine.has_errors());
assert_eq!(engine.error_count(), 1);
}
#[test]
fn test_diag_engine_warning() {
let mut engine = X86ClangDiagnosticEngine::new();
engine.warning(X86ClangStage::Sema, "unused variable");
assert!(!engine.has_errors());
assert_eq!(engine.warning_count(), 1);
}
#[test]
fn test_diag_engine_fatal() {
let mut engine = X86ClangDiagnosticEngine::new();
engine.fatal(X86ClangStage::Init, "cannot open file");
assert!(engine.has_fatal());
}
#[test]
fn test_diag_engine_error_limit() {
let mut engine = X86ClangDiagnosticEngine::new();
engine.error_limit = 3;
for i in 0..10 {
engine.error(X86ClangStage::Parse, &format!("error {}", i));
}
assert_eq!(engine.error_count(), 3);
}
#[test]
fn test_diag_engine_clear() {
let mut engine = X86ClangDiagnosticEngine::new();
engine.error(X86ClangStage::Parse, "err");
engine.clear();
assert!(!engine.has_errors());
}
#[test]
fn test_diag_engine_drain() {
let mut engine = X86ClangDiagnosticEngine::new();
engine.error(X86ClangStage::Parse, "err1");
engine.error(X86ClangStage::Parse, "err2");
let drained = engine.drain();
assert_eq!(drained.len(), 2);
assert!(!engine.has_errors());
}
#[test]
fn test_diag_engine_format_all() {
let mut engine = X86ClangDiagnosticEngine::new();
engine.error(X86ClangStage::Parse, "test error");
let formatted = engine.format_all();
assert!(formatted.contains("error"));
assert!(formatted.contains("test error"));
}
#[test]
fn test_diag_engine_note() {
let mut engine = X86ClangDiagnosticEngine::new();
engine.note(X86ClangStage::Sema, "candidate function here");
assert_eq!(engine.note_count(), 1);
assert!(!engine.has_errors());
}
#[test]
fn test_stats_creation() {
let stats = X86ClangStats::new();
assert_eq!(stats.total_compilations, 0);
assert_eq!(stats.cache_hit_rate(), 0.0);
}
#[test]
fn test_stats_record_stage() {
let mut stats = X86ClangStats::new();
let result = X86ClangStageResult::success(X86ClangStage::Parse)
.with_duration(Duration::from_millis(10));
stats.record_stage(&result);
assert_eq!(stats.stage_counts.get(&X86ClangStage::Parse), Some(&1));
}
#[test]
fn test_stats_record_success_failure() {
let mut stats = X86ClangStats::new();
stats.record_success();
stats.record_success();
stats.record_failure();
assert_eq!(stats.total_compilations, 3);
assert_eq!(stats.successful_compilations, 2);
assert_eq!(stats.failed_compilations, 1);
}
#[test]
fn test_stats_cache_hit_rate() {
let mut stats = X86ClangStats::new();
stats.cache_hits = 7;
stats.cache_misses = 3;
assert!((stats.cache_hit_rate() - 0.7).abs() < 0.01);
}
#[test]
fn test_stats_format_report() {
let mut stats = X86ClangStats::new();
stats.total_compilations = 5;
stats.successful_compilations = 4;
stats.cache_hits = 2;
stats.cache_misses = 3;
let report = stats.format_report();
assert!(report.contains("5 total"));
assert!(report.contains("40.0%"));
}
#[test]
fn test_pipeline_creation() {
let opts = X86ClangCompileOptions::x86_64_linux();
let pipeline = X86ClangPipeline::new(opts);
assert_eq!(pipeline.stage_results.len(), 0);
assert!(pipeline.diagnostics.has_no_errors());
}
#[test]
fn test_pipeline_run_empty_source() {
let opts = X86ClangCompileOptions::x86_64_linux();
let mut pipeline = X86ClangPipeline::new(opts);
let result = pipeline.run("");
assert!(!result.stage_results.is_empty());
}
#[test]
fn test_pipeline_run_trivial() {
let opts = X86ClangCompileOptions::x86_64_linux();
let mut pipeline = X86ClangPipeline::new(opts);
let result = pipeline.run("int main(void) { return 0; }");
assert!(!result.stage_results.is_empty());
}
#[test]
fn test_pipeline_stop_after_parse() {
let mut opts = X86ClangCompileOptions::x86_64_linux();
opts.stop_after = Some(X86ClangStage::Parse);
let mut pipeline = X86ClangPipeline::new(opts);
let result = pipeline.run("int x;");
assert_eq!(result.completed_stage, Some(X86ClangStage::Parse));
}
#[test]
fn test_pipeline_stop_after_sema() {
let mut opts = X86ClangCompileOptions::x86_64_linux();
opts.stop_after = Some(X86ClangStage::Sema);
let mut pipeline = X86ClangPipeline::new(opts);
let result = pipeline.run("int x;");
assert_eq!(result.completed_stage, Some(X86ClangStage::Sema));
}
#[test]
fn test_pipeline_report() {
let opts = X86ClangCompileOptions::x86_64_linux();
let mut pipeline = X86ClangPipeline::new(opts);
let _ = pipeline.run("int x;");
let report = pipeline.report();
assert!(report.contains("X86ClangPipeline"));
}
#[test]
fn test_pipeline_diagnostics_collected() {
let mut opts = X86ClangCompileOptions::x86_64_linux();
opts.stop_after = Some(X86ClangStage::Parse);
let mut pipeline = X86ClangPipeline::new(opts);
let _ = pipeline.run("invalid syntax @@@");
let _count = pipeline.diagnostics.error_count();
}
#[test]
fn test_x86_clang_compile() {
let result = x86_clang_compile("int x;");
assert!(!result.stage_results.is_empty());
}
#[test]
fn test_x86_clang_compile_with_opts() {
let opts = X86ClangCompileOptions::x86_64_linux_release();
let result = x86_clang_compile_with_opts("int x;", opts);
assert!(!result.stage_results.is_empty());
}
#[test]
fn test_x86_clang_compile_stop_after() {
let result = x86_clang_compile_stop_after("int x;", X86ClangStage::Preprocess);
assert_eq!(result.completed_stage, Some(X86ClangStage::Preprocess));
}
#[test]
fn test_make_x86_release_pipeline() {
let pipeline = make_x86_release_pipeline();
assert_eq!(pipeline.options.opt_level, X86OptLevel::O3);
}
#[test]
fn test_make_x86_32_pipeline() {
let pipeline = make_x86_32_pipeline();
assert!(pipeline.options.target_triple.contains("i386"));
}
#[test]
fn test_full_pipeline_with_optimization() {
let mut opts = X86ClangCompileOptions::x86_64_linux();
opts.opt_level = X86OptLevel::O2;
let mut pipeline = X86ClangPipeline::new(opts);
let result = pipeline.run("int f(void) { return 42; }");
let report = result.format_report();
assert!(report.contains("optimize"));
}
#[test]
fn test_pipeline_all_stages_no_error() {
let mut opts = X86ClangCompileOptions::x86_64_linux();
opts.opt_level = X86OptLevel::O0;
opts.error_limit = 100;
let mut pipeline = X86ClangPipeline::new(opts);
let result = pipeline.run("int main(void) { return 0; }");
let stage_names: Vec<String> = result
.stage_results
.iter()
.map(|r| r.stage.name().to_string())
.collect();
assert!(stage_names.contains(&"init".to_string()));
assert!(stage_names.contains(&"preprocess".to_string()));
}
#[test]
fn test_pipeline_with_werror() {
let mut opts = X86ClangCompileOptions::x86_64_linux();
opts.werror = true;
opts.stop_after = Some(X86ClangStage::Parse);
let mut pipeline = X86ClangPipeline::new(opts);
let _ = pipeline.run("int x;");
}
#[test]
fn test_pipeline_cache_enabled() {
let mut opts = X86ClangCompileOptions::x86_64_linux();
opts.enable_cache = true;
opts.stop_after = Some(X86ClangStage::Parse);
let mut pipeline = X86ClangPipeline::new(opts);
let _ = pipeline.run("int x;");
}
#[test]
fn test_pipeline_with_debug_info() {
let mut opts = X86ClangCompileOptions::x86_64_linux();
opts.debug_info = true;
opts.stop_after = Some(X86ClangStage::IRGen);
let mut pipeline = X86ClangPipeline::new(opts);
let result = pipeline.run("int x;");
assert!(result.success || !result.success); }
#[test]
fn stress_pipeline_many_compiles() {
let opts = X86ClangCompileOptions::x86_64_linux();
let mut pipeline = X86ClangPipeline::new(opts);
for i in 0..50 {
let source = format!("int var{} = {};", i, i);
let result = pipeline.run(&source);
if !result.success {
}
}
}
#[test]
fn stress_pipeline_stage_ordering() {
for _ in 0..20 {
let stages = X86ClangStage::all();
for i in 1..stages.len() {
let prev = stages[i - 1];
let curr = stages[i];
assert_eq!(prev.next(), Some(curr));
assert_eq!(curr.prev(), Some(prev));
}
}
}
#[test]
fn smoke_all_diag_levels_display() {
let levels = [
X86ClangDiagLevel::Note,
X86ClangDiagLevel::Warning,
X86ClangDiagLevel::Error,
X86ClangDiagLevel::Fatal,
];
for level in &levels {
let s = level.to_string();
assert!(!s.is_empty());
}
}
#[test]
fn smoke_all_stages_have_unique_names() {
use std::collections::HashSet;
let mut names = HashSet::new();
for stage in X86ClangStage::all() {
assert!(
names.insert(stage.name().to_string()),
"Duplicate: {}",
stage.name()
);
}
}
#[test]
fn test_pipeline_with_many_defines() {
let mut opts = X86ClangCompileOptions::x86_64_linux();
opts.defines = (0..100)
.map(|i| (format!("VAR{}", i), Some(format!("{}", i))))
.collect();
opts.stop_after = Some(X86ClangStage::Preprocess);
let mut pipeline = X86ClangPipeline::new(opts);
let result = pipeline.run("int x = VAR0;");
assert!(!result.stage_results.is_empty());
}
#[test]
fn test_pipeline_with_many_includes() {
let mut opts = X86ClangCompileOptions::x86_64_linux();
opts.include_paths = (0..50).map(|i| format!("/usr/include/path{}", i)).collect();
opts.stop_after = Some(X86ClangStage::Parse);
let mut pipeline = X86ClangPipeline::new(opts);
let result = pipeline.run("int x;");
assert!(!result.stage_results.is_empty());
}
#[test]
fn test_incremental_tracker_full_workflow() {
let mut tracker = X86IncrementalTracker::new();
let files = vec!["a.c".into(), "b.c".into(), "c.c".into()];
tracker.detect_changes(&files, |f| Some(format!("hash_{}_v1", f)));
assert!(tracker.has_changes());
assert_eq!(tracker.added_files.len(), 3);
tracker.detect_changes(&files, |f| Some(format!("hash_{}_v1", f)));
assert!(!tracker.has_changes());
let files_mod = vec!["a.c".into(), "b.c".into(), "c.c".into()];
tracker.detect_changes(&files_mod, |f| {
if f == "b.c" {
Some("hash_b_v2".into())
} else {
Some(format!("hash_{}_v1", f))
}
});
assert!(tracker.has_changes());
assert!(tracker.changed_files.contains(&"b.c".to_string()));
let files_rm = vec!["a.c".into(), "c.c".into()];
tracker.detect_changes(&files_rm, |f| Some(format!("hash_{}_v1", f)));
assert!(tracker.removed_files.contains(&"b.c".to_string()));
}
#[test]
fn test_incremental_tracker_affected_units_dedup() {
let mut tracker = X86IncrementalTracker::new();
tracker.changed_files.push("a.c".into());
tracker.changed_files.push("a.c".into()); tracker.added_files.push("b.c".into());
let affected = tracker.affected_units();
assert_eq!(affected.len(), 2); }
#[test]
fn test_incremental_tracker_force_recompile() {
let mut tracker = X86IncrementalTracker::new();
assert!(!tracker.needs_full_recompile());
tracker.recompile_all = true;
assert!(tracker.needs_full_recompile());
}
#[test]
fn test_diag_engine_stage_filters() {
let mut engine = X86ClangDiagnosticEngine::new();
engine.stage_filters = vec![X86ClangStage::Sema];
engine.error(X86ClangStage::Parse, "parse error");
engine.error(X86ClangStage::Sema, "sema error");
let sema_errors = engine
.diagnostics
.iter()
.filter(|d| d.stage == X86ClangStage::Sema)
.count();
assert_eq!(sema_errors, 1);
}
#[test]
fn test_diag_engine_suppress_notes() {
let mut engine = X86ClangDiagnosticEngine::new();
engine.suppress_notes = true;
engine.note(X86ClangStage::Sema, "note 1");
assert_eq!(engine.note_count(), 0);
}
#[test]
fn test_diag_engine_werror() {
let mut engine = X86ClangDiagnosticEngine::new();
engine.werror = true;
engine.warning(X86ClangStage::Sema, "warning");
assert!(engine.warning_count() > 0);
}
#[test]
fn test_diag_level_ordering() {
assert!(X86ClangDiagLevel::Note < X86ClangDiagLevel::Warning);
assert!(X86ClangDiagLevel::Warning < X86ClangDiagLevel::Error);
assert!(X86ClangDiagLevel::Error < X86ClangDiagLevel::Fatal);
}
#[test]
fn test_diag_with_all_fields() {
let diag = X86ClangDiagnostic {
level: X86ClangDiagLevel::Error,
message: "test".into(),
file: Some("test.c".into()),
line: 42,
column: 7,
stage: X86ClangStage::Parse,
code: Some("E0001".into()),
fixit: Some("add semicolon".into()),
};
let s = diag.to_string();
assert!(s.contains("test.c"));
assert!(s.contains("42"));
assert!(s.contains("E0001"));
}
#[test]
fn test_stats_record_multiple_stages() {
let mut stats = X86ClangStats::new();
for stage in X86ClangStage::all() {
let result =
X86ClangStageResult::success(stage).with_duration(Duration::from_micros(100));
stats.record_stage(&result);
}
for stage in X86ClangStage::all() {
assert_eq!(stats.stage_counts.get(&stage), Some(&1));
}
}
#[test]
fn test_stats_report_formatting() {
let mut stats = X86ClangStats::new();
stats.total_compilations = 42;
stats.successful_compilations = 40;
stats.failed_compilations = 2;
stats.cache_hits = 30;
stats.cache_misses = 12;
let report = stats.format_report();
assert!(report.contains("42 total"));
assert!(report.contains("40 ok"));
assert!(report.contains("71.4%")); }
#[test]
fn test_x86_clang_result_new() {
let result = X86ClangResult::new();
assert!(result.success);
assert!(result.stage_results.is_empty());
assert_eq!(result.total_duration, Duration::ZERO);
}
#[test]
fn test_x86_clang_result_success_with_all_fields() {
let sr = vec![
X86ClangStageResult::success(X86ClangStage::Init)
.with_duration(Duration::from_millis(1)),
X86ClangStageResult::success(X86ClangStage::Parse)
.with_duration(Duration::from_millis(5)),
];
let result = X86ClangResult::success_with(sr, "output".into());
assert!(result.success);
assert_eq!(result.completed_stage, Some(X86ClangStage::Parse));
assert!(result.total_duration >= Duration::from_millis(6));
}
#[test]
fn test_stage_is_frontend_all() {
for stage in X86ClangStage::frontend_stages() {
assert!(stage.is_frontend());
assert!(!stage.is_backend());
}
}
#[test]
fn test_stage_is_backend_all() {
for stage in X86ClangStage::backend_stages() {
assert!(stage.is_backend());
assert!(!stage.is_frontend());
}
}
#[test]
fn test_stage_next_prev_roundtrip() {
for stage in X86ClangStage::all() {
if let Some(next) = stage.next() {
assert_eq!(next.prev(), Some(stage));
}
if let Some(prev) = stage.prev() {
assert_eq!(prev.next(), Some(stage));
}
}
}
#[test]
fn test_pipeline_all_output_formats() {
let formats = [
X86OutputFormat::PreprocessedSource,
X86OutputFormat::LLVMIR,
X86OutputFormat::LLVMBC,
X86OutputFormat::Assembly,
X86OutputFormat::Object,
X86OutputFormat::SharedLib,
X86OutputFormat::Executable,
];
for &fmt in &formats {
let mut opts = X86ClangCompileOptions::x86_64_linux();
opts.output_format = fmt;
opts.stop_after = Some(X86ClangStage::Parse);
let mut pipeline = X86ClangPipeline::new(opts);
let result = pipeline.run("int x;");
assert!(!result.stage_results.is_empty());
}
}
#[test]
fn test_pipeline_all_opt_levels() {
let levels = [
X86OptLevel::O0,
X86OptLevel::O1,
X86OptLevel::O2,
X86OptLevel::O3,
X86OptLevel::Os,
X86OptLevel::Oz,
];
for &level in &levels {
let mut opts = X86ClangCompileOptions::x86_64_linux();
opts.opt_level = level;
opts.stop_after = Some(X86ClangStage::Parse);
let mut pipeline = X86ClangPipeline::new(opts);
let result = pipeline.run("int x;");
assert!(!result.stage_results.is_empty());
}
}
#[test]
fn test_pipeline_with_c_standards() {
let standards = [
CLangStandard::C89,
CLangStandard::C99,
CLangStandard::C11,
CLangStandard::C17,
CLangStandard::C23,
];
for &std in &standards {
let mut opts = X86ClangCompileOptions::x86_64_linux();
opts.standard = std;
opts.stop_after = Some(X86ClangStage::Parse);
let mut pipeline = X86ClangPipeline::new(opts);
let result = pipeline.run("int x;");
assert!(!result.stage_results.is_empty());
}
}
#[test]
fn test_pipeline_all_code_models() {
let models = [
CodeModel::Default,
CodeModel::Small,
CodeModel::Kernel,
CodeModel::Medium,
CodeModel::Large,
];
for &model in &models {
let mut opts = X86ClangCompileOptions::x86_64_linux();
opts.code_model = model;
let pipeline = X86ClangPipeline::new(opts);
assert_eq!(pipeline.options.code_model, model);
}
}
#[test]
fn test_pipeline_all_reloc_models() {
let models = [RelocModel::Default, RelocModel::Static, RelocModel::PIC];
for &model in &models {
let mut opts = X86ClangCompileOptions::x86_64_linux();
opts.reloc_model = model;
opts.stop_after = Some(X86ClangStage::Parse);
let mut pipeline = X86ClangPipeline::new(opts);
let result = pipeline.run("int x;");
assert!(!result.stage_results.is_empty());
}
}
#[test]
fn test_pipeline_with_all_flags() {
let mut opts = X86ClangCompileOptions::x86_64_linux();
opts.wall = true;
opts.wextra = true;
opts.werror = true;
opts.pedantic = true;
opts.verbose = true;
opts.debug_info = true;
opts.pic = true;
opts.pie = true;
opts.static_linking = true;
opts.time_trace = true;
opts.print_stats = true;
opts.enable_cache = true;
opts.stop_after = Some(X86ClangStage::Parse);
let mut pipeline = X86ClangPipeline::new(opts);
let result = pipeline.run("int x;");
assert!(!result.stage_results.is_empty());
}
#[test]
fn stress_pipeline_large_source() {
let mut source = String::from("int main(void) {\n");
for i in 0..500 {
source.push_str(&format!("int var{} = {};\n", i, i));
}
source.push_str("return 0; }");
let result = x86_clang_compile(&source);
assert!(!result.stage_results.is_empty());
}
#[test]
fn stress_diagnostic_engine_many_diags() {
let mut engine = X86ClangDiagnosticEngine::new();
engine.error_limit = usize::MAX;
for i in 0..1000 {
engine.error(X86ClangStage::Parse, &format!("error {}", i));
}
assert_eq!(engine.error_count(), 1000);
engine.clear();
assert_eq!(engine.error_count(), 0);
}
#[test]
fn stress_pipeline_repeated_runs() {
let mut opts = X86ClangCompileOptions::x86_64_linux();
opts.stop_after = Some(X86ClangStage::Parse);
let mut pipeline = X86ClangPipeline::new(opts);
for _ in 0..100 {
let result = pipeline.run("int x;");
assert!(!result.stage_results.is_empty());
}
}
#[test]
fn test_pipeline_with_many_defines_v2() {
let mut opts = X86ClangCompileOptions::x86_64_linux();
opts.defines = (0..100)
.map(|i| (format!("VAR{}", i), Some(format!("{}", i))))
.collect();
opts.stop_after = Some(X86ClangStage::Preprocess);
let mut pipeline = X86ClangPipeline::new(opts);
let result = pipeline.run("int x = VAR0;");
assert!(!result.stage_results.is_empty());
}
#[test]
fn test_pipeline_with_many_includes_v2() {
let mut opts = X86ClangCompileOptions::x86_64_linux();
opts.include_paths = (0..50).map(|i| format!("/usr/include/path{}", i)).collect();
opts.stop_after = Some(X86ClangStage::Parse);
let mut pipeline = X86ClangPipeline::new(opts);
let result = pipeline.run("int x;");
assert!(!result.stage_results.is_empty());
}
#[test]
fn test_incremental_tracker_full_workflow_v2() {
let mut tracker = X86IncrementalTracker::new();
let files = vec!["a.c".into(), "b.c".into(), "c.c".into()];
tracker.detect_changes(&files, |f| Some(format!("hash_{}_v1", f)));
assert!(tracker.has_changes());
assert_eq!(tracker.added_files.len(), 3);
tracker.detect_changes(&files, |f| Some(format!("hash_{}_v1", f)));
assert!(!tracker.has_changes());
let files_mod = vec!["a.c".into(), "b.c".into(), "c.c".into()];
tracker.detect_changes(&files_mod, |f| {
if f == "b.c" {
Some("hash_b_v2".into())
} else {
Some(format!("hash_{}_v1", f))
}
});
assert!(tracker.has_changes());
assert!(tracker.changed_files.contains(&"b.c".to_string()));
let files_rm = vec!["a.c".into(), "c.c".into()];
tracker.detect_changes(&files_rm, |f| Some(format!("hash_{}_v1", f)));
assert!(tracker.removed_files.contains(&"b.c".to_string()));
}
#[test]
fn test_incremental_tracker_affected_units_dedup_v2() {
let mut tracker = X86IncrementalTracker::new();
tracker.changed_files.push("a.c".into());
tracker.changed_files.push("a.c".into());
tracker.added_files.push("b.c".into());
let affected = tracker.affected_units();
assert_eq!(affected.len(), 2);
}
#[test]
fn test_incremental_tracker_force_recompile_v2() {
let mut tracker = X86IncrementalTracker::new();
assert!(!tracker.needs_full_recompile());
tracker.recompile_all = true;
assert!(tracker.needs_full_recompile());
}
#[test]
fn test_diag_engine_stage_filters_v2() {
let mut engine = X86ClangDiagnosticEngine::new();
engine.stage_filters = vec![X86ClangStage::Sema];
engine.error(X86ClangStage::Parse, "parse error");
engine.error(X86ClangStage::Sema, "sema error");
let sema_errors = engine
.diagnostics
.iter()
.filter(|d| d.stage == X86ClangStage::Sema)
.count();
assert_eq!(sema_errors, 1);
}
#[test]
fn test_diag_engine_suppress_notes_v2() {
let mut engine = X86ClangDiagnosticEngine::new();
engine.suppress_notes = true;
engine.note(X86ClangStage::Sema, "note 1");
assert_eq!(engine.note_count(), 0);
}
#[test]
fn test_diag_engine_werror_v2() {
let mut engine = X86ClangDiagnosticEngine::new();
engine.werror = true;
engine.warning(X86ClangStage::Sema, "warning");
assert!(engine.warning_count() > 0);
}
#[test]
fn test_diag_level_ordering_v2() {
assert!(X86ClangDiagLevel::Note < X86ClangDiagLevel::Warning);
assert!(X86ClangDiagLevel::Warning < X86ClangDiagLevel::Error);
assert!(X86ClangDiagLevel::Error < X86ClangDiagLevel::Fatal);
}
#[test]
fn test_diag_with_all_fields_v2() {
let diag = X86ClangDiagnostic {
level: X86ClangDiagLevel::Error,
message: "test".into(),
file: Some("test.c".into()),
line: 42,
column: 7,
stage: X86ClangStage::Parse,
code: Some("E0001".into()),
fixit: Some("add semicolon".into()),
};
let s = diag.to_string();
assert!(s.contains("test.c"));
assert!(s.contains("42"));
assert!(s.contains("E0001"));
}
#[test]
fn test_stats_record_multiple_stages_v2() {
let mut stats = X86ClangStats::new();
for stage in X86ClangStage::all() {
let result =
X86ClangStageResult::success(stage).with_duration(Duration::from_micros(100));
stats.record_stage(&result);
}
for stage in X86ClangStage::all() {
assert_eq!(stats.stage_counts.get(&stage), Some(&1));
}
}
#[test]
fn test_stats_report_formatting_v2() {
let mut stats = X86ClangStats::new();
stats.total_compilations = 42;
stats.successful_compilations = 40;
stats.failed_compilations = 2;
stats.cache_hits = 30;
stats.cache_misses = 12;
let report = stats.format_report();
assert!(report.contains("42 total"));
assert!(report.contains("40 ok"));
}
#[test]
fn test_x86_clang_result_new_v2() {
let result = X86ClangResult::new();
assert!(result.success);
assert!(result.stage_results.is_empty());
assert_eq!(result.total_duration, Duration::ZERO);
}
#[test]
fn test_x86_clang_result_success_with_all_fields_v2() {
let sr = vec![
X86ClangStageResult::success(X86ClangStage::Init)
.with_duration(Duration::from_millis(1)),
X86ClangStageResult::success(X86ClangStage::Parse)
.with_duration(Duration::from_millis(5)),
];
let result = X86ClangResult::success_with(sr, "output".into());
assert!(result.success);
assert_eq!(result.completed_stage, Some(X86ClangStage::Parse));
assert!(result.total_duration >= Duration::from_millis(6));
}
#[test]
fn test_stage_is_frontend_all_v2() {
for stage in X86ClangStage::frontend_stages() {
assert!(stage.is_frontend());
assert!(!stage.is_backend());
}
}
#[test]
fn test_stage_is_backend_all_v2() {
for stage in X86ClangStage::backend_stages() {
assert!(stage.is_backend());
assert!(!stage.is_frontend());
}
}
#[test]
fn test_stage_next_prev_roundtrip_v2() {
for stage in X86ClangStage::all() {
if let Some(next) = stage.next() {
assert_eq!(next.prev(), Some(stage));
}
if let Some(prev) = stage.prev() {
assert_eq!(prev.next(), Some(stage));
}
}
}
#[test]
fn test_pipeline_all_output_formats_v2() {
let formats = [
X86OutputFormat::PreprocessedSource,
X86OutputFormat::LLVMIR,
X86OutputFormat::LLVMBC,
X86OutputFormat::Assembly,
X86OutputFormat::Object,
X86OutputFormat::SharedLib,
X86OutputFormat::Executable,
];
for &fmt in &formats {
let mut opts = X86ClangCompileOptions::x86_64_linux();
opts.output_format = fmt;
opts.stop_after = Some(X86ClangStage::Parse);
let mut pipeline = X86ClangPipeline::new(opts);
let result = pipeline.run("int x;");
assert!(!result.stage_results.is_empty());
}
}
#[test]
fn test_pipeline_all_opt_levels_v2() {
let levels = [
X86OptLevel::O0,
X86OptLevel::O1,
X86OptLevel::O2,
X86OptLevel::O3,
X86OptLevel::Os,
X86OptLevel::Oz,
];
for &level in &levels {
let mut opts = X86ClangCompileOptions::x86_64_linux();
opts.opt_level = level;
opts.stop_after = Some(X86ClangStage::Parse);
let mut pipeline = X86ClangPipeline::new(opts);
let result = pipeline.run("int x;");
assert!(!result.stage_results.is_empty());
}
}
#[test]
fn test_pipeline_with_c_standards_v2() {
let standards = [
CLangStandard::C89,
CLangStandard::C99,
CLangStandard::C11,
CLangStandard::C17,
CLangStandard::C23,
];
for &std in &standards {
let mut opts = X86ClangCompileOptions::x86_64_linux();
opts.standard = std;
opts.stop_after = Some(X86ClangStage::Parse);
let mut pipeline = X86ClangPipeline::new(opts);
let result = pipeline.run("int x;");
assert!(!result.stage_results.is_empty());
}
}
#[test]
fn test_pipeline_all_code_models_v2() {
let models = [
CodeModel::Default,
CodeModel::Small,
CodeModel::Kernel,
CodeModel::Medium,
CodeModel::Large,
];
for &model in &models {
let mut opts = X86ClangCompileOptions::x86_64_linux();
opts.code_model = model;
let pipeline = X86ClangPipeline::new(opts);
assert_eq!(pipeline.options.code_model, model);
}
}
#[test]
fn test_pipeline_all_reloc_models_v2() {
let models = [RelocModel::Default, RelocModel::Static, RelocModel::PIC];
for &model in &models {
let mut opts = X86ClangCompileOptions::x86_64_linux();
opts.reloc_model = model;
opts.stop_after = Some(X86ClangStage::Parse);
let mut pipeline = X86ClangPipeline::new(opts);
let result = pipeline.run("int x;");
assert!(!result.stage_results.is_empty());
}
}
#[test]
fn test_pipeline_with_all_flags_v2() {
let mut opts = X86ClangCompileOptions::x86_64_linux();
opts.wall = true;
opts.wextra = true;
opts.werror = true;
opts.pedantic = true;
opts.verbose = true;
opts.debug_info = true;
opts.pic = true;
opts.pie = true;
opts.static_linking = true;
opts.time_trace = true;
opts.print_stats = true;
opts.enable_cache = true;
opts.stop_after = Some(X86ClangStage::Parse);
let mut pipeline = X86ClangPipeline::new(opts);
let result = pipeline.run("int x;");
assert!(!result.stage_results.is_empty());
}
#[test]
fn stress_pipeline_large_source_v2() {
let mut source = String::from("int main(void) {\n");
for i in 0..500 {
source.push_str(&format!("int var{} = {};\n", i, i));
}
source.push_str("return 0; }");
let result = x86_clang_compile(&source);
assert!(!result.stage_results.is_empty());
}
#[test]
fn stress_diagnostic_engine_many_diags_v2() {
let mut engine = X86ClangDiagnosticEngine::new();
engine.error_limit = usize::MAX;
for i in 0..1000 {
engine.error(X86ClangStage::Parse, &format!("error {}", i));
}
assert_eq!(engine.error_count(), 1000);
engine.clear();
assert_eq!(engine.error_count(), 0);
}
#[test]
fn bulk_stage_frontend1() {
assert!(X86ClangStage::Preprocess.is_frontend());
}
#[test]
fn bulk_stage_frontend2() {
assert!(X86ClangStage::Lex.is_frontend());
}
#[test]
fn bulk_stage_frontend3() {
assert!(X86ClangStage::Parse.is_frontend());
}
#[test]
fn bulk_stage_frontend4() {
assert!(X86ClangStage::Sema.is_frontend());
}
#[test]
fn bulk_stage_backend1() {
assert!(X86ClangStage::CodeGen.is_backend());
}
#[test]
fn bulk_stage_backend2() {
assert!(X86ClangStage::ISel.is_backend());
}
#[test]
fn bulk_stage_backend3() {
assert!(X86ClangStage::RegAlloc.is_backend());
}
#[test]
fn bulk_stage_backend4() {
assert!(X86ClangStage::FrameLower.is_backend());
}
#[test]
fn bulk_stage_backend5() {
assert!(X86ClangStage::Encode.is_backend());
}
#[test]
fn bulk_stage_middle1() {
assert!(X86ClangStage::IRGen.is_middle());
}
#[test]
fn bulk_stage_middle2() {
assert!(X86ClangStage::Optimize.is_middle());
}
#[test]
fn bulk_result_default_success() {
assert!(X86ClangResult::default().success);
}
#[test]
fn bulk_result_default_errors() {
assert_eq!(X86ClangResult::default().total_errors, 0);
}
#[test]
fn bulk_result_default_warnings() {
assert_eq!(X86ClangResult::default().total_warnings, 0);
}
#[test]
fn bulk_result_default_hits() {
assert_eq!(X86ClangResult::default().cache_hits, 0);
}
#[test]
fn bulk_incremental_default() {
assert!(!X86IncrementalTracker::default().has_changes());
}
#[test]
fn bulk_diag_default() {
assert!(!X86ClangDiagnosticEngine::default().has_errors());
}
#[test]
fn bulk_diag_has_no_errors() {
assert!(X86ClangDiagnosticEngine::new().has_no_errors());
}
#[test]
fn bulk_stats_default_comps() {
assert_eq!(X86ClangStats::default().total_compilations, 0);
}
#[test]
fn bulk_stats_default_cache() {
assert_eq!(X86ClangStats::default().cache_hit_rate(), 0.0);
}
#[test]
fn bulk_pipeline_creation() {
let _ = X86ClangPipeline::new(X86ClangCompileOptions::x86_64_linux());
}
#[test]
fn bulk_pipeline_from_opts() {
let _ = X86ClangPipeline::from_x86_compile_options(X86CompileOptions::x86_64_linux());
}
#[test]
fn bulk_stage_result_line() {
assert!(X86ClangStageResult::success(X86ClangStage::Init)
.one_line()
.contains("init"));
}
#[test]
fn bulk_stage_result_check() {
assert!(X86ClangStageResult::success(X86ClangStage::Parse)
.one_line()
.contains("\u{2713}"));
}
#[test]
fn bulk_opts_default_triple() {
assert!(X86ClangCompileOptions::default()
.target_triple
.contains("x86_64"));
}
#[test]
fn bulk_opts_release_o3() {
assert_eq!(
X86ClangCompileOptions::x86_64_linux_release().opt_level,
X86OptLevel::O3
);
}
#[test]
fn bulk_opts_32_i386() {
assert!(X86ClangCompileOptions::x86_32_linux()
.target_triple
.contains("i386"));
}
#[test]
fn bulk_stage_next_init() {
assert_eq!(X86ClangStage::Init.next(), Some(X86ClangStage::Preprocess));
}
#[test]
fn bulk_stage_prev_emit() {
assert_eq!(X86ClangStage::Emit.prev(), Some(X86ClangStage::Link));
}
#[test]
fn bulk_result_format() {
assert!(X86ClangResult::default()
.format_report()
.contains("\u{2550}"));
}
#[test]
fn bulk_stats_format() {
assert!(X86ClangStats::default()
.format_report()
.contains("\u{2550}"));
}
#[test]
fn bulk_incr_clear() {
let mut t = X86IncrementalTracker::new();
t.added_files.push("a.c".into());
t.clear();
assert!(t.added_files.is_empty());
}
#[test]
fn bulk_stage_all_display() {
for s in X86ClangStage::all() {
let _ = s.to_string();
}
}
}