use std::collections::{HashMap, HashSet};
use std::fs;
use std::io::{BufReader, BufWriter, Read, Write};
use std::path::{Path, PathBuf};
use crate::clang::cpp_modules::{
BmiFile, GlobalModuleFragment, HeaderUnitSynthesizer, ModuleDecl, ModuleDependencyGraph,
ModuleImport, ModuleKind, ModuleMap, ModuleMapFile, ModuleName, ModuleOwnership,
ModuleValidator, PrivateModuleFragment,
};
use crate::clang::{CLangStandard, ClangOptions};
use crate::x86::{
x86_calling_convention::X86CallingConvention, x86_frame_lowering::X86FrameLowering,
x86_target_machine::X86TargetMachine, X86_STACK_ALIGNMENT_64,
};
pub const X86_MODULE_CACHE_DIR: &str = ".x86_module_cache";
pub const X86_BMI_EXTENSION: &str = ".pcm";
pub const X86_MODULE_MAP_FILE: &str = "module.modulemap";
pub const X86_CACHE_MAX_VERSIONS: usize = 5;
pub const X86_CACHE_MAX_SIZE_BYTES: u64 = 1_073_741_824;
pub const X86_MODULE_HASH_ALGORITHM: &str = "sha256";
pub const X86_BMI_SIGNATURE_MAGIC: &[u8; 4] = b"X86M";
pub const X86_BMI_EXTENDED_VERSION: u32 = 2;
pub struct X86Modules {
pub compiler: X86ModuleCompiler,
pub module_map: X86ModuleMap,
pub dependency: X86ModuleDependency,
pub bmi_transfer: X86BMITransfer,
pub cache: X86ModuleCache,
pub ownership: X86ModuleOwnership,
pub linkage: X86ModuleLinkage,
pub import_handler: X86ModuleImport,
pub std_modules: X86ModuleStandardLibrary,
pub target_machine: X86TargetMachine,
pub calling_convention: X86CallingConvention,
pub frame_lowering: X86FrameLowering,
pub options: ClangOptions,
pub modules_enabled: bool,
pending_units: Vec<X86CompilationUnit>,
stats: X86ModuleStats,
}
#[derive(Debug, Clone, Default)]
pub struct X86ModuleStats {
pub modules_compiled: usize,
pub bmis_imported: usize,
pub bmis_exported: usize,
pub cache_hits: usize,
pub cache_misses: usize,
pub total_bmi_bytes: u64,
pub compilation_time_ms: u64,
}
#[derive(Debug, Clone)]
pub struct X86CompilationUnit {
pub source_path: PathBuf,
pub module_decl: Option<ModuleDecl>,
pub unit_kind: X86CompilationUnitKind,
pub target_triple: String,
pub include_paths: Vec<PathBuf>,
pub defines: Vec<(String, Option<String>)>,
pub required_imports: Vec<ModuleImport>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86CompilationUnitKind {
ModuleInterface,
ModuleImplementation,
ModulePartitionInterface,
ModulePartitionImplementation,
HeaderUnit,
NonModular,
GlobalFragment,
}
impl X86Modules {
pub fn new(options: ClangOptions, target_machine: X86TargetMachine) -> Self {
Self {
compiler: X86ModuleCompiler::new(options.clone()),
module_map: X86ModuleMap::new(),
dependency: X86ModuleDependency::new(),
bmi_transfer: X86BMITransfer::new(),
cache: X86ModuleCache::new(&options),
ownership: X86ModuleOwnership::new(),
linkage: X86ModuleLinkage::new(&options),
import_handler: X86ModuleImport::new(),
std_modules: X86ModuleStandardLibrary::new(),
target_machine,
calling_convention: X86CallingConvention::X86_64_SysV,
frame_lowering: X86FrameLowering::default(),
options,
modules_enabled: true,
pending_units: Vec::new(),
stats: X86ModuleStats::default(),
}
}
pub fn set_modules_enabled(&mut self, enabled: bool) {
self.modules_enabled = enabled;
}
pub fn add_compilation_unit(&mut self, unit: X86CompilationUnit) {
self.pending_units.push(unit);
}
pub fn add_compilation_units(&mut self, units: impl IntoIterator<Item = X86CompilationUnit>) {
self.pending_units.extend(units);
}
pub fn process_all_units(&mut self) -> Result<Vec<X86CompilationResult>, String> {
if !self.modules_enabled {
return Ok(Vec::new());
}
self.dependency
.build_from_units(&self.pending_units, &self.module_map);
if let Some(cycle) = self.dependency.detect_cycle() {
return Err(format!(
"circular module dependency detected: {}",
cycle.join(" -> ")
));
}
let order = self.dependency.topological_sort()?;
let mut results = Vec::new();
let unit_by_name: HashMap<String, usize> = self
.pending_units
.iter()
.enumerate()
.filter_map(|(i, u)| u.module_decl.as_ref().map(|m| (m.name.to_string(), i)))
.collect();
for unit_name in &order {
if let Some(idx) = unit_by_name.get(unit_name) {
let unit = self.pending_units[*idx].clone();
let result = self.compile_unit(&unit)?;
results.push(result);
}
}
self.pending_units.clear();
Ok(results)
}
fn compile_unit(&mut self, unit: &X86CompilationUnit) -> Result<X86CompilationResult, String> {
if let Some(decl) = &unit.module_decl {
let module_name = decl.name.to_string();
if let Some(cached_bmi) = self.cache.lookup(&module_name) {
self.stats.cache_hits += 1;
return Ok(X86CompilationResult {
module_name: module_name.clone(),
bmi_path: Some(cached_bmi),
from_cache: true,
unit_kind: unit.unit_kind,
});
}
self.stats.cache_misses += 1;
}
let bmi = self
.compiler
.compile_unit(unit, &self.module_map, &self.import_handler)?;
if bmi.bmi_path.is_some() {
self.cache.store(unit, &bmi)?;
}
if let Some(decl) = &unit.module_decl {
self.ownership
.register_module(decl.name.clone(), unit.unit_kind);
self.linkage
.register_module(&decl.name, unit.unit_kind, &self.options);
}
Ok(bmi)
}
pub fn import_module(&mut self, import: &ModuleImport) -> Result<(), String> {
self.import_handler
.resolve_import(import, &self.module_map, &self.cache)?;
if let Some(dep) = self.module_map.resolve_name(&import.name.to_string()) {
let current = self
.pending_units
.last()
.and_then(|u| u.module_decl.as_ref())
.map(|d| d.name.to_string())
.unwrap_or_default();
if !current.is_empty() {
self.dependency.add_dependency(¤t, &dep);
}
}
Ok(())
}
pub fn stats(&self) -> &X86ModuleStats {
&self.stats
}
pub fn reset(&mut self) {
self.compiler = X86ModuleCompiler::new(self.options.clone());
self.module_map = X86ModuleMap::new();
self.dependency = X86ModuleDependency::new();
self.bmi_transfer = X86BMITransfer::new();
self.cache = X86ModuleCache::new(&self.options);
self.ownership = X86ModuleOwnership::new();
self.linkage = X86ModuleLinkage::new(&self.options);
self.import_handler = X86ModuleImport::new();
self.pending_units.clear();
self.stats = X86ModuleStats::default();
}
pub fn validate(&self) -> Vec<String> {
let mut errors = Vec::new();
errors.extend(self.dependency.validate());
errors.extend(self.module_map.validate());
errors.extend(self.ownership.validate());
errors.extend(self.linkage.validate());
errors
}
}
#[derive(Debug, Clone)]
pub struct X86CompilationResult {
pub module_name: String,
pub bmi_path: Option<PathBuf>,
pub from_cache: bool,
pub unit_kind: X86CompilationUnitKind,
}
impl X86CompilationResult {
pub fn new(
module_name: &str,
bmi_path: Option<PathBuf>,
from_cache: bool,
unit_kind: X86CompilationUnitKind,
) -> Self {
Self {
module_name: module_name.to_string(),
bmi_path,
from_cache,
unit_kind,
}
}
}
pub struct X86ModuleCompiler {
pub verbose: bool,
pub output_dir: PathBuf,
pub standard: CLangStandard,
parsed_decls: Vec<ModuleDecl>,
current_global_fragment: Option<GlobalModuleFragment>,
current_private_fragment: Option<PrivateModuleFragment>,
header_synthesizer: HeaderUnitSynthesizer,
partition_registry: HashMap<(String, String), X86CompilationUnit>,
active_module: Option<ModuleDecl>,
errors: Vec<String>,
validator: ModuleValidator,
}
impl X86ModuleCompiler {
pub fn new(options: ClangOptions) -> Self {
Self {
verbose: options.verbose,
output_dir: PathBuf::from(X86_MODULE_CACHE_DIR),
standard: options.standard,
parsed_decls: Vec::new(),
current_global_fragment: None,
current_private_fragment: None,
header_synthesizer: HeaderUnitSynthesizer::new(options.includes.clone()),
partition_registry: HashMap::new(),
active_module: None,
errors: Vec::new(),
validator: ModuleValidator::new(),
}
}
pub fn set_output_dir(&mut self, dir: &Path) {
self.output_dir = dir.to_path_buf();
}
pub fn parse_module_decl(&mut self, source: &str) -> Option<ModuleDecl> {
let decl = crate::clang::cpp_modules::parse_module_decl(source)?;
if !self.validator.validate_module_decl(&decl) {
self.errors.extend(self.validator.errors().iter().cloned());
return None;
}
self.active_module = Some(decl.clone());
self.parsed_decls.push(decl.clone());
Some(decl)
}
pub fn parse_import_decl(&mut self, source: &str) -> Option<ModuleImport> {
let import = crate::clang::cpp_modules::parse_import_decl(source)?;
Some(import)
}
pub fn register_partition(
&mut self,
module_name: &str,
partition_name: &str,
unit: X86CompilationUnit,
) {
self.partition_registry
.insert((module_name.to_string(), partition_name.to_string()), unit);
}
pub fn get_partition(
&self,
module_name: &str,
partition_name: &str,
) -> Option<&X86CompilationUnit> {
self.partition_registry
.get(&(module_name.to_string(), partition_name.to_string()))
}
pub fn has_partition(&self, module_name: &str, partition_name: &str) -> bool {
self.partition_registry
.contains_key(&(module_name.to_string(), partition_name.to_string()))
}
pub fn partitions_of(&self, module_name: &str) -> Vec<String> {
let prefix = module_name.to_string();
self.partition_registry
.keys()
.filter(|(m, _)| m == &prefix)
.map(|(_, p)| p.clone())
.collect()
}
pub fn begin_global_fragment(&mut self) -> Result<(), String> {
if self.active_module.is_some() {
return Err(
"global module fragment must appear before any module declaration".to_string(),
);
}
if self.current_global_fragment.is_some() {
return Err("global module fragment already open".to_string());
}
self.current_global_fragment = Some(GlobalModuleFragment::new());
Ok(())
}
pub fn add_to_global_fragment(&mut self, decl: &str) -> Result<(), String> {
if let Some(ref mut gmf) = self.current_global_fragment {
gmf.add_decl(decl);
Ok(())
} else {
Err("no global module fragment is active".to_string())
}
}
pub fn add_include_in_global_fragment(&mut self, header: &str) -> Result<(), String> {
if let Some(ref mut gmf) = self.current_global_fragment {
gmf.add_include(header);
Ok(())
} else {
Err("no global module fragment is active".to_string())
}
}
pub fn end_global_fragment(&mut self) -> Result<Option<GlobalModuleFragment>, String> {
let gmf = self.current_global_fragment.take();
if gmf.as_ref().map(|g| g.is_empty()).unwrap_or(true) {
self.errors.push(
"global module fragment must contain at least one declaration or include".into(),
);
}
Ok(gmf)
}
pub fn begin_private_fragment(&mut self) -> Result<(), String> {
if self.active_module.is_none() {
return Err(
"private module fragment must appear inside a module interface unit".to_string(),
);
}
if self.current_private_fragment.is_some() {
return Err("private module fragment already open".to_string());
}
let active = self.active_module.as_ref().unwrap();
if !active.kind.is_exported() {
return Err(
"private module fragment is only allowed in module interface units".to_string(),
);
}
self.current_private_fragment = Some(PrivateModuleFragment::new());
Ok(())
}
pub fn add_to_private_fragment(&mut self, decl: &str) -> Result<(), String> {
if let Some(ref mut pmf) = self.current_private_fragment {
pmf.add_decl(decl);
Ok(())
} else {
Err("no private module fragment is active".to_string())
}
}
pub fn end_private_fragment(&mut self) -> Result<(), String> {
if let Some(mut pmf) = self.current_private_fragment.take() {
pmf.close();
Ok(())
} else {
Err("no private module fragment to close".to_string())
}
}
pub fn process_header_unit(&mut self, header_name: &str) -> Result<ModuleDecl, String> {
let info = self.header_synthesizer.synthesize_header_unit(header_name);
let name = info.name.clone();
let decl = ModuleDecl {
name,
kind: ModuleKind::ModuleInterface,
partition: None,
source_loc: Some(format!("<{}>", header_name)),
};
Ok(decl)
}
pub fn synthesize_header_bmi(&mut self, header: &str) -> String {
self.header_synthesizer.generate_synthesized_bmi(header)
}
pub fn is_header_synthesized(&self, header: &str) -> bool {
self.header_synthesizer.is_synthesized(header)
}
pub fn compile_unit(
&mut self,
unit: &X86CompilationUnit,
module_map: &X86ModuleMap,
import_handler: &X86ModuleImport,
) -> Result<X86CompilationResult, String> {
let decl = unit
.module_decl
.as_ref()
.ok_or("compilation unit has no module declaration")?;
let module_name = decl.name.to_string();
match unit.unit_kind {
X86CompilationUnitKind::ModuleInterface
| X86CompilationUnitKind::ModulePartitionInterface => {
let bmi_path = self.output_dir.join(format!(
"{}{}",
module_name.replace(':', "_"),
X86_BMI_EXTENSION
));
for import in &unit.required_imports {
if !import.is_header_unit {
let name = import.name.to_string();
if let Some(resolved) = module_map.resolve_name(&name) {
if !import_handler.is_import_resolved(&resolved) {
return Err(format!(
"module '{}' requires unresolved import '{}'",
module_name, name
));
}
} else {
return Err(format!(
"module '{}' imports unknown module '{}'",
module_name, name
));
}
}
}
if !self.output_dir.exists() {
fs::create_dir_all(&self.output_dir)
.map_err(|e| format!("failed to create output directory: {}", e))?;
}
let bmi_content = self.emit_bmi_content(decl, &unit.required_imports);
fs::write(&bmi_path, &bmi_content)
.map_err(|e| format!("failed to write BMI: {}", e))?;
if self.verbose {
eprintln!(
"info: compiled module interface '{}' → {}",
module_name,
bmi_path.display()
);
}
Ok(X86CompilationResult::new(
&module_name,
Some(bmi_path),
false,
unit.unit_kind,
))
}
X86CompilationUnitKind::ModuleImplementation
| X86CompilationUnitKind::ModulePartitionImplementation => {
if self.verbose {
eprintln!("info: compiled module implementation '{}'", module_name);
}
Ok(X86CompilationResult::new(
&module_name,
None,
false,
unit.unit_kind,
))
}
X86CompilationUnitKind::HeaderUnit => {
let bmi_path = self.output_dir.join(format!(
"{}{}",
module_name.replace(['/', '\\', '.', '<', '>'], "_"),
X86_BMI_EXTENSION
));
let bmi_content = self.synthesize_header_bmi(&module_name);
fs::write(&bmi_path, &bmi_content)
.map_err(|e| format!("failed to write header BMI: {}", e))?;
Ok(X86CompilationResult::new(
&module_name,
Some(bmi_path),
false,
unit.unit_kind,
))
}
X86CompilationUnitKind::NonModular => {
Ok(X86CompilationResult::new(
&module_name,
None,
false,
unit.unit_kind,
))
}
X86CompilationUnitKind::GlobalFragment => {
Err("global fragment units are part of a module, not standalone".to_string())
}
}
}
fn emit_bmi_content(&self, decl: &ModuleDecl, imports: &[ModuleImport]) -> Vec<u8> {
let bmi = BmiFile {
module_name: decl.name.clone(),
is_interface: decl.kind.is_exported(),
dependencies: imports.iter().map(|i| i.name.clone()).collect(),
ast_data: Vec::new(),
lookup_table: Vec::new(),
source_map: Vec::new(),
module_version: None,
};
bmi.serialize()
}
pub fn validate(&self) -> bool {
self.errors.is_empty()
}
pub fn errors(&self) -> &[String] {
&self.errors
}
pub fn clear_errors(&mut self) {
self.errors.clear();
}
pub fn active_module(&self) -> Option<&ModuleDecl> {
self.active_module.as_ref()
}
pub fn parsed_declarations(&self) -> &[ModuleDecl] {
&self.parsed_decls
}
}
#[derive(Debug, Clone)]
pub struct X86ModuleMap {
module_map_files: HashMap<PathBuf, ModuleMapFile>,
module_index: HashMap<String, X86ModuleMapEntry>,
submodule_hierarchy: HashMap<String, Vec<String>>,
extern_modules: HashSet<String>,
framework_modules: HashMap<String, PathBuf>,
link_libraries: HashMap<String, Vec<String>>,
search_paths: Vec<PathBuf>,
validated: bool,
errors: Vec<String>,
}
#[derive(Debug, Clone)]
pub struct X86ModuleMapEntry {
pub module_name: String,
pub umbrella: Option<String>,
pub headers: Vec<String>,
pub excluded_headers: Vec<String>,
pub submodules: Vec<String>,
pub is_framework: bool,
pub is_explicit: bool,
pub exports: Vec<String>,
pub link_libraries: Vec<String>,
pub base_dir: PathBuf,
pub is_extern: bool,
pub parent: Option<String>,
pub x86_config_flags: Vec<String>,
pub darwin_specific: X86DarwinModuleFlags,
}
#[derive(Debug, Clone, Default)]
pub struct X86DarwinModuleFlags {
pub is_framework_bundle: bool,
pub framework_version: Option<String>,
pub use_system_cache: bool,
pub is_system_module: bool,
pub requires_explicit_build: bool,
}
impl X86ModuleMapEntry {
pub fn new(name: &str, base_dir: &Path) -> Self {
Self {
module_name: name.to_string(),
umbrella: None,
headers: Vec::new(),
excluded_headers: Vec::new(),
submodules: Vec::new(),
is_framework: false,
is_explicit: false,
exports: Vec::new(),
link_libraries: Vec::new(),
base_dir: base_dir.to_path_buf(),
is_extern: false,
parent: None,
x86_config_flags: Vec::new(),
darwin_specific: X86DarwinModuleFlags::default(),
}
}
pub fn has_submodules(&self) -> bool {
!self.submodules.is_empty()
}
pub fn fully_qualified_name(&self) -> String {
if let Some(ref parent) = self.parent {
format!("{}.{}", parent, self.module_name)
} else {
self.module_name.clone()
}
}
}
impl X86ModuleMap {
pub fn new() -> Self {
Self {
module_map_files: HashMap::new(),
module_index: HashMap::new(),
submodule_hierarchy: HashMap::new(),
extern_modules: HashSet::new(),
framework_modules: HashMap::new(),
link_libraries: HashMap::new(),
search_paths: Vec::new(),
validated: false,
errors: Vec::new(),
}
}
pub fn add_search_path(&mut self, path: &Path) {
self.search_paths.push(path.to_path_buf());
}
pub fn add_search_paths(&mut self, paths: impl IntoIterator<Item = PathBuf>) {
self.search_paths.extend(paths);
}
pub fn parse_module_map_file(&mut self, path: &Path) -> Result<(), String> {
let content = fs::read_to_string(path)
.map_err(|e| format!("failed to read module map '{}': {}", path.display(), e))?;
let base_dir = path.parent().unwrap_or(Path::new(".")).to_path_buf();
let map_file = ModuleMapFile::parse(&content, &base_dir.to_string_lossy())?;
for entry in &map_file.modules {
let x86_entry = X86ModuleMapEntry {
module_name: entry.module_name.clone(),
umbrella: entry.umbrella.clone(),
headers: entry.headers.clone(),
excluded_headers: entry.excluded_headers.clone(),
submodules: entry
.submodules
.iter()
.map(|s| s.module_name.clone())
.collect(),
is_framework: entry.is_framework,
is_explicit: entry.is_explicit,
exports: entry.exports.clone(),
link_libraries: entry.link_libraries.clone(),
base_dir: base_dir.clone(),
is_extern: false,
parent: None,
x86_config_flags: Vec::new(),
darwin_specific: X86DarwinModuleFlags::default(),
};
if !x86_entry.submodules.is_empty() {
self.submodule_hierarchy
.insert(entry.module_name.clone(), x86_entry.submodules.clone());
}
if !x86_entry.link_libraries.is_empty() {
self.link_libraries
.insert(entry.module_name.clone(), x86_entry.link_libraries.clone());
}
if x86_entry.is_framework {
self.framework_modules
.insert(entry.module_name.clone(), base_dir.clone());
}
self.module_index
.insert(entry.module_name.clone(), x86_entry);
}
self.module_map_files.insert(base_dir.clone(), map_file);
self.validated = false; Ok(())
}
pub fn discover_module_maps(&mut self) -> Result<usize, String> {
let mut count = 0;
for search_path in &self.search_paths.clone() {
let map_path = search_path.join(X86_MODULE_MAP_FILE);
if map_path.exists() {
self.parse_module_map_file(&map_path)?;
count += 1;
}
if let Ok(entries) = fs::read_dir(search_path) {
for entry in entries.flatten() {
if entry.file_type().map(|t| t.is_dir()).unwrap_or(false) {
let sub_map = entry.path().join(X86_MODULE_MAP_FILE);
if sub_map.exists() {
self.parse_module_map_file(&sub_map)?;
count += 1;
}
}
}
}
}
Ok(count)
}
pub fn resolve_name(&self, name: &str) -> Option<String> {
if self.module_index.contains_key(name) {
return Some(name.to_string());
}
for (parent, children) in &self.submodule_hierarchy {
for child in children {
let full_name = format!("{}.{}", parent, child);
if full_name == name {
return Some(name.to_string());
}
}
}
if self.extern_modules.contains(name) {
return Some(name.to_string());
}
None
}
pub fn get_module(&self, name: &str) -> Option<&X86ModuleMapEntry> {
self.module_index.get(name)
}
pub fn submodules_of(&self, parent: &str) -> Vec<&str> {
self.submodule_hierarchy
.get(parent)
.map(|v| v.iter().map(|s| s.as_str()).collect())
.unwrap_or_default()
}
pub fn register_submodule(&mut self, parent: &str, child: &str) {
self.submodule_hierarchy
.entry(parent.to_string())
.or_default()
.push(child.to_string());
}
pub fn register_extern_module(&mut self, name: &str) {
self.extern_modules.insert(name.to_string());
}
pub fn is_extern_module(&self, name: &str) -> bool {
self.extern_modules.contains(name)
}
pub fn register_framework_module(&mut self, name: &str, path: &Path) {
self.framework_modules
.insert(name.to_string(), path.to_path_buf());
let entry = X86ModuleMapEntry {
module_name: name.to_string(),
umbrella: None,
headers: Vec::new(),
excluded_headers: Vec::new(),
submodules: Vec::new(),
is_framework: true,
is_explicit: false,
exports: Vec::new(),
link_libraries: Vec::new(),
base_dir: path.to_path_buf(),
is_extern: false,
parent: None,
x86_config_flags: Vec::new(),
darwin_specific: X86DarwinModuleFlags {
is_framework_bundle: true,
framework_version: None,
use_system_cache: true,
is_system_module: true,
requires_explicit_build: false,
},
};
self.module_index.insert(name.to_string(), entry);
}
pub fn is_framework_module(&self, name: &str) -> bool {
self.framework_modules.contains_key(name)
}
pub fn framework_path(&self, name: &str) -> Option<&PathBuf> {
self.framework_modules.get(name)
}
pub fn link_libraries_for(&self, module_name: &str) -> Vec<&str> {
self.link_libraries
.get(module_name)
.map(|v| v.iter().map(|s| s.as_str()).collect())
.unwrap_or_default()
}
pub fn register_link_library(&mut self, module_name: &str, library: &str) {
self.link_libraries
.entry(module_name.to_string())
.or_default()
.push(library.to_string());
}
pub fn all_module_names(&self) -> Vec<&str> {
self.module_index.keys().map(|s| s.as_str()).collect()
}
pub fn contains_module(&self, name: &str) -> bool {
self.module_index.contains_key(name)
}
pub fn module_headers(&self, name: &str) -> Vec<PathBuf> {
if let Some(entry) = self.module_index.get(name) {
entry
.headers
.iter()
.map(|h| entry.base_dir.join(h))
.collect()
} else {
Vec::new()
}
}
pub fn module_umbrella(&self, name: &str) -> Option<PathBuf> {
self.module_index
.get(name)
.and_then(|e| e.umbrella.as_ref())
.map(|u| {
if let Some(e) = self.module_index.get(name) {
e.base_dir.join(u)
} else {
PathBuf::from(u)
}
})
}
pub fn to_base_module_map(&self) -> ModuleMap {
let mut map = ModuleMap::new();
for (name, _) in &self.module_index {
let info = crate::clang::cpp_modules::ModuleInfo::new(ModuleName::from_str(name), true);
map.register_module(info);
}
for (parent, children) in &self.submodule_hierarchy {
for child in children {
let full_child = format!("{}.{}", parent, child);
map.add_import(parent, &full_child);
}
}
map
}
pub fn validate(&self) -> Vec<String> {
let mut errors = Vec::new();
let mut name_dirs: HashMap<&str, Vec<&PathBuf>> = HashMap::new();
for entry in self.module_index.values() {
name_dirs
.entry(&entry.module_name)
.or_default()
.push(&entry.base_dir);
}
for (name, dirs) in &name_dirs {
if dirs.len() > 1 {
errors.push(format!(
"module '{}' defined in multiple locations: {:?}",
name, dirs
));
}
}
for (parent, children) in &self.submodule_hierarchy {
if !self.module_index.contains_key(parent) {
errors.push(format!(
"parent module '{}' not found for submodules: {:?}",
parent, children
));
}
}
for (name, path) in &self.framework_modules {
if !path.exists() {
errors.push(format!(
"framework module '{}' path does not exist: {}",
name,
path.display()
));
}
}
errors
}
pub fn serialize_to_string(&self) -> String {
let mut out = String::new();
out.push_str("// X86 Module Map — auto-generated\n\n");
for entry in self.module_index.values() {
if entry.is_framework {
out.push_str("framework module ");
} else if entry.is_extern {
out.push_str("extern module ");
} else if entry.is_explicit {
out.push_str("explicit module ");
} else {
out.push_str("module ");
}
out.push_str(&entry.module_name);
out.push_str(" {\n");
if let Some(ref umbrella) = entry.umbrella {
out.push_str(&format!(" umbrella \"{}\"\n", umbrella));
}
for header in &entry.headers {
out.push_str(&format!(" header \"{}\"\n", header));
}
for excluded in &entry.excluded_headers {
out.push_str(&format!(" excluded_header \"{}\"\n", excluded));
}
for sub in &entry.submodules {
out.push_str(&format!(" module {}\n", sub));
}
for export in &entry.exports {
out.push_str(&format!(" export {}\n", export));
}
for lib in &entry.link_libraries {
out.push_str(&format!(" link \"{}\"\n", lib));
}
if entry.is_explicit {
out.push_str(" explicit\n");
}
out.push_str("}\n\n");
}
for (parent, children) in &self.submodule_hierarchy {
if self.module_index.contains_key(parent) && !children.is_empty() {
out.push_str(&format!(
"// Submodules of {}: {}\n",
parent,
children.join(", ")
));
}
}
out
}
pub fn write_to_file(&self, path: &Path) -> Result<(), String> {
let content = self.serialize_to_string();
fs::write(path, &content).map_err(|e| format!("failed to write module map: {}", e))
}
}
impl Default for X86ModuleMap {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct X86ModuleDependency {
pub graph: ModuleDependencyGraph,
reverse_edges: HashMap<String, HashSet<String>>,
node_status: HashMap<String, X86DependencyStatus>,
timestamps: HashMap<String, X86FileTimestamps>,
previous_build_order: Vec<String>,
needs_full_rebuild: bool,
pending_changes: Vec<X86DependencyChange>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86DependencyStatus {
Pending,
InProgress,
Compiled,
Failed,
Skipped,
UpToDate,
}
#[derive(Debug, Clone)]
pub struct X86FileTimestamps {
pub source_mtime: u64,
pub bmi_mtime: u64,
pub deps_mtime: u64,
pub source_hash: u64,
pub flags_hash: u64,
}
#[derive(Debug, Clone)]
pub enum X86DependencyChange {
Added(String),
Removed(String),
SourceChanged(String),
FlagsChanged(String),
EdgeAdded(String, String),
EdgeRemoved(String, String),
}
impl X86ModuleDependency {
pub fn new() -> Self {
Self {
graph: ModuleDependencyGraph::new(),
reverse_edges: HashMap::new(),
node_status: HashMap::new(),
timestamps: HashMap::new(),
previous_build_order: Vec::new(),
needs_full_rebuild: true,
pending_changes: Vec::new(),
}
}
pub fn build_from_units(&mut self, units: &[X86CompilationUnit], module_map: &X86ModuleMap) {
for unit in units {
if let Some(ref decl) = unit.module_decl {
let name = decl.name.to_string();
self.node_status
.entry(name.clone())
.or_insert(X86DependencyStatus::Pending);
for import in &unit.required_imports {
if !import.is_header_unit {
let dep_name = import.name.to_string();
if let Some(resolved) = module_map.resolve_name(&dep_name) {
self.add_dependency(&name, &resolved);
} else {
self.add_dependency(&name, &dep_name);
}
}
}
if let Some(entry) = module_map.get_module(&name) {
for export in &entry.exports {
if let Some(resolved) = module_map.resolve_name(export) {
self.add_dependency(&name, &resolved);
}
}
}
}
}
}
pub fn add_dependency(&mut self, module: &str, dependency: &str) {
if module == dependency {
return; }
self.graph.add_dependency(module, dependency);
self.reverse_edges
.entry(dependency.to_string())
.or_default()
.insert(module.to_string());
self.needs_full_rebuild = true;
}
pub fn remove_dependency(&mut self, module: &str, dependency: &str) {
self.reverse_edges
.get_mut(dependency)
.map(|set| set.remove(module));
self.needs_full_rebuild = true;
}
pub fn direct_dependencies(&self, module: &str) -> Vec<String> {
self.graph.transitive_deps(module).into_iter().collect()
}
pub fn dependents_of(&self, module: &str) -> Vec<String> {
self.reverse_edges
.get(module)
.cloned()
.unwrap_or_default()
.into_iter()
.collect()
}
pub fn topological_sort(&self) -> Result<Vec<String>, String> {
self.graph.topological_sort()
}
pub fn detect_cycle(&self) -> Option<Vec<String>> {
match self.topological_sort() {
Ok(_) => None,
Err(_) => {
self.find_cycle_dfs()
}
}
}
fn find_cycle_dfs(&self) -> Option<Vec<String>> {
let all_nodes: HashSet<String> = self.reverse_edges.keys().cloned().collect();
#[derive(Clone, Copy, PartialEq, Eq)]
enum Color {
White,
Gray,
Black,
}
let mut colors: HashMap<String, Color> = all_nodes
.iter()
.map(|n| (n.clone(), Color::White))
.collect();
let mut parent: HashMap<String, String> = HashMap::new();
for node in &all_nodes {
if colors[node] == Color::White {
let mut stack = vec![(node.clone(), Color::Gray)];
colors.insert(node.clone(), Color::Gray);
while let Some((current, _)) = stack.last().cloned() {
let neighbors = self.direct_dependencies(¤t);
let mut found_unvisited = false;
for neighbor in &neighbors {
let neighbor_color = colors.get(neighbor).copied().unwrap_or(Color::White);
match neighbor_color {
Color::White => {
colors.insert(neighbor.clone(), Color::Gray);
parent.insert(neighbor.clone(), current.clone());
stack.push((neighbor.clone(), Color::Gray));
found_unvisited = true;
break;
}
Color::Gray => {
let mut cycle = vec![neighbor.clone(), current.clone()];
let mut node = current.clone();
while let Some(p) = parent.get(&node) {
if p == neighbor {
cycle.push(p.clone());
break;
}
cycle.push(p.clone());
node = p.clone();
}
cycle.reverse();
return Some(cycle);
}
Color::Black => { }
}
}
if !found_unvisited {
stack.pop();
colors.insert(current, Color::Black);
}
}
}
}
None
}
pub fn compute_incremental_rebuild(&mut self, changed_files: &[String]) -> Vec<String> {
let mut to_rebuild = Vec::new();
for changed in changed_files {
for (module, _) in &self.node_status {
if changed.contains(module) {
to_rebuild.push(module.clone());
}
}
if let Some(dependents) = self.reverse_edges.get(changed) {
for dep in dependents {
to_rebuild.push(dep.clone());
}
}
}
to_rebuild.sort();
to_rebuild.dedup();
to_rebuild
}
pub fn record_timestamp(&mut self, module: &str, timestamps: X86FileTimestamps) {
self.timestamps.insert(module.to_string(), timestamps);
}
pub fn needs_rebuild(&self, module: &str) -> bool {
if self.needs_full_rebuild {
return true;
}
if let Some(ts) = self.timestamps.get(module) {
for dep in self.direct_dependencies(module) {
if let Some(dep_ts) = self.timestamps.get(&dep) {
if dep_ts.bmi_mtime > ts.source_mtime {
return true;
}
}
}
false
} else {
true
}
}
pub fn set_status(&mut self, module: &str, status: X86DependencyStatus) {
self.node_status.insert(module.to_string(), status);
}
pub fn status(&self, module: &str) -> Option<X86DependencyStatus> {
self.node_status.get(module).copied()
}
pub fn record_change(&mut self, change: X86DependencyChange) {
self.pending_changes.push(change);
self.needs_full_rebuild = true;
}
pub fn pending_changes(&self) -> &[X86DependencyChange] {
&self.pending_changes
}
pub fn clear_pending_changes(&mut self) {
self.pending_changes.clear();
}
pub fn has_cycles(&self) -> bool {
self.detect_cycle().is_some()
}
pub fn node_count(&self) -> usize {
self.node_status.len()
}
pub fn edge_count(&self) -> usize {
self.reverse_edges.values().map(|v| v.len()).sum()
}
pub fn transitive_reduction(&mut self) -> Vec<(String, String)> {
self.graph.find_redundant_edges()
}
pub fn validate(&self) -> Vec<String> {
let mut errors = Vec::new();
let from_deps: HashSet<&str> = self
.reverse_edges
.values()
.flatten()
.map(|s| s.as_str())
.collect();
for (module, _) in &self.node_status {
if !self.reverse_edges.contains_key(module) && !from_deps.contains(module.as_str()) {
}
for dep in self.direct_dependencies(module) {
if !self.node_status.contains_key(&dep) {
errors.push(format!(
"module '{}' depends on unknown module '{}'",
module, dep
));
}
}
}
errors
}
pub fn to_dot(&self) -> String {
let mut dot = String::from("digraph ModuleDependencies {\n");
dot.push_str(" rankdir=LR;\n");
dot.push_str(" node [shape=box];\n");
let mut seen_edges = HashSet::new();
for (module, _) in &self.node_status {
let color = match self.node_status.get(module) {
Some(X86DependencyStatus::Compiled) => "green",
Some(X86DependencyStatus::UpToDate) => "blue",
Some(X86DependencyStatus::Failed) => "red",
Some(X86DependencyStatus::InProgress) => "yellow",
_ => "black",
};
dot.push_str(&format!(
" \"{}\" [color={}, style=filled, fillcolor=lightgrey];\n",
module, color
));
for dep in self.direct_dependencies(module) {
let edge = (module.clone(), dep.clone());
if seen_edges.insert(edge.clone()) {
dot.push_str(&format!(" \"{}\" -> \"{}\";\n", edge.0, edge.1));
}
}
}
dot.push_str("}\n");
dot
}
}
impl Default for X86ModuleDependency {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct X86BMITransfer {
pub enable_compression: bool,
pub embed_target_info: bool,
pub verify_signatures: bool,
pub target_triple: String,
pub compiler_version: String,
signature_keys: X86BMISignatureKeys,
operation_log: Vec<X86BMIOperation>,
}
#[derive(Debug, Clone)]
pub struct X86BMISignatureKeys {
pub include_source_hash: bool,
pub include_flags: bool,
pub include_target: bool,
pub include_dependency_hashes: bool,
}
#[derive(Debug, Clone)]
pub struct X86BMIOperation {
pub op: X86BMIOpKind,
pub module_name: String,
pub file_size: usize,
pub success: bool,
pub error: Option<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86BMIOpKind {
Export,
Import,
Verify,
Update,
Remove,
}
#[derive(Debug, Clone)]
pub struct X86BmiFile {
pub base: BmiFile,
pub x86_target_triple: String,
pub x86_compiler_version: String,
pub embedded_module_map: Option<String>,
pub x86_flags: X86BMIFlags,
pub signature: [u8; 32],
}
#[derive(Debug, Clone, Default)]
pub struct X86BMIFlags {
pub sse_enabled: bool,
pub sse2_enabled: bool,
pub avx_enabled: bool,
pub avx2_enabled: bool,
pub avx512_enabled: bool,
pub is_64bit: bool,
pub microsoft_abi: bool,
pub stack_alignment: u32,
pub rtti_enabled: bool,
pub exceptions_enabled: bool,
}
impl X86BMITransfer {
pub fn new() -> Self {
Self {
enable_compression: true,
embed_target_info: true,
verify_signatures: true,
target_triple: "x86_64-unknown-linux-gnu".to_string(),
compiler_version: env!("CARGO_PKG_VERSION").to_string(),
signature_keys: X86BMISignatureKeys {
include_source_hash: true,
include_flags: true,
include_target: true,
include_dependency_hashes: true,
},
operation_log: Vec::new(),
}
}
pub fn set_target_triple(&mut self, triple: &str) {
self.target_triple = triple.to_string();
}
pub fn export_bmi(
&mut self,
module_name: &str,
_decl: &ModuleDecl,
bmi_file: &BmiFile,
output_path: &Path,
_options: &ClangOptions,
) -> Result<usize, String> {
let x86_bmi = self.build_x86_bmi(bmi_file);
let data = self.serialize_x86_bmi(&x86_bmi);
if let Some(parent) = output_path.parent() {
fs::create_dir_all(parent)
.map_err(|e| format!("failed to create BMI output directory: {}", e))?;
}
let mut file = BufWriter::new(
fs::File::create(output_path)
.map_err(|e| format!("failed to create BMI file: {}", e))?,
);
file.write_all(&data)
.map_err(|e| format!("failed to write BMI data: {}", e))?;
file.flush()
.map_err(|e| format!("failed to flush BMI data: {}", e))?;
let size = data.len();
self.operation_log.push(X86BMIOperation {
op: X86BMIOpKind::Export,
module_name: module_name.to_string(),
file_size: size,
success: true,
error: None,
});
Ok(size)
}
pub fn import_bmi(&mut self, path: &Path) -> Result<X86BmiFile, String> {
let mut file = BufReader::new(
fs::File::open(path)
.map_err(|e| format!("failed to open BMI file '{}': {}", path.display(), e))?,
);
let mut data = Vec::new();
file.read_to_end(&mut data)
.map_err(|e| format!("failed to read BMI data: {}", e))?;
self.deserialize_x86_bmi(&data).map_err(|e| {
self.operation_log.push(X86BMIOperation {
op: X86BMIOpKind::Import,
module_name: path.to_string_lossy().to_string(),
file_size: data.len(),
success: false,
error: Some(e.clone()),
});
e
})
}
fn build_x86_bmi(&self, base: &BmiFile) -> X86BmiFile {
let mut x86_bmi = X86BmiFile {
base: base.clone(),
x86_target_triple: self.target_triple.clone(),
x86_compiler_version: self.compiler_version.clone(),
embedded_module_map: None,
x86_flags: X86BMIFlags {
sse_enabled: true,
sse2_enabled: true,
avx_enabled: false,
avx2_enabled: false,
avx512_enabled: false,
is_64bit: self.target_triple.contains("64"),
microsoft_abi: self.target_triple.contains("windows"),
stack_alignment: X86_STACK_ALIGNMENT_64,
rtti_enabled: true,
exceptions_enabled: true,
},
signature: [0u8; 32],
};
let sig = self.compute_signature(&x86_bmi);
x86_bmi.signature = sig;
x86_bmi
}
pub fn serialize_x86_bmi(&self, bmi: &X86BmiFile) -> Vec<u8> {
let mut buf = Vec::new();
buf.extend_from_slice(X86_BMI_SIGNATURE_MAGIC);
buf.extend_from_slice(&X86_BMI_EXTENDED_VERSION.to_le_bytes());
let base_data = bmi.base.serialize();
buf.extend_from_slice(&(base_data.len() as u64).to_le_bytes());
buf.extend_from_slice(&base_data);
buf.extend_from_slice(&(bmi.x86_target_triple.len() as u32).to_le_bytes());
buf.extend_from_slice(bmi.x86_target_triple.as_bytes());
buf.extend_from_slice(&(bmi.x86_compiler_version.len() as u32).to_le_bytes());
buf.extend_from_slice(bmi.x86_compiler_version.as_bytes());
if let Some(ref map) = bmi.embedded_module_map {
buf.push(1);
buf.extend_from_slice(&(map.len() as u64).to_le_bytes());
buf.extend_from_slice(map.as_bytes());
} else {
buf.push(0);
}
let mut flags: u64 = 0;
if bmi.x86_flags.sse_enabled {
flags |= 1 << 0;
}
if bmi.x86_flags.sse2_enabled {
flags |= 1 << 1;
}
if bmi.x86_flags.avx_enabled {
flags |= 1 << 2;
}
if bmi.x86_flags.avx2_enabled {
flags |= 1 << 3;
}
if bmi.x86_flags.avx512_enabled {
flags |= 1 << 4;
}
if bmi.x86_flags.is_64bit {
flags |= 1 << 5;
}
if bmi.x86_flags.microsoft_abi {
flags |= 1 << 6;
}
if bmi.x86_flags.rtti_enabled {
flags |= 1 << 7;
}
if bmi.x86_flags.exceptions_enabled {
flags |= 1 << 8;
}
buf.extend_from_slice(&flags.to_le_bytes());
buf.extend_from_slice(&bmi.x86_flags.stack_alignment.to_le_bytes());
buf.extend_from_slice(&bmi.signature);
buf
}
pub fn deserialize_x86_bmi(&self, data: &[u8]) -> Result<X86BmiFile, String> {
if data.len() < 12 {
return Err("X86 BMI data too short".to_string());
}
let mut pos = 0usize;
if &data[pos..pos + 4] != X86_BMI_SIGNATURE_MAGIC {
return Err("invalid X86 BMI magic bytes".to_string());
}
pos += 4;
let ext_version =
u32::from_le_bytes([data[pos], data[pos + 1], data[pos + 2], data[pos + 3]]);
pos += 4;
if ext_version > X86_BMI_EXTENDED_VERSION {
return Err(format!(
"unsupported X86 BMI version {} (expected <= {})",
ext_version, X86_BMI_EXTENDED_VERSION
));
}
if pos + 8 > data.len() {
return Err("truncated base BMI size field".to_string());
}
let base_len = u64::from_le_bytes([
data[pos],
data[pos + 1],
data[pos + 2],
data[pos + 3],
data[pos + 4],
data[pos + 5],
data[pos + 6],
data[pos + 7],
]) as usize;
pos += 8;
if pos + base_len > data.len() {
return Err("truncated base BMI data".to_string());
}
let base = BmiFile::deserialize(&data[pos..pos + base_len])?;
pos += base_len;
if pos + 4 > data.len() {
return Err("truncated target triple length".to_string());
}
let triple_len =
u32::from_le_bytes([data[pos], data[pos + 1], data[pos + 2], data[pos + 3]]) as usize;
pos += 4;
if pos + triple_len > data.len() {
return Err("truncated target triple data".to_string());
}
let x86_target_triple = String::from_utf8(data[pos..pos + triple_len].to_vec())
.map_err(|e| format!("invalid UTF-8 in target triple: {}", e))?;
pos += triple_len;
if pos + 4 > data.len() {
return Err("truncated compiler version length".to_string());
}
let ver_len =
u32::from_le_bytes([data[pos], data[pos + 1], data[pos + 2], data[pos + 3]]) as usize;
pos += 4;
if pos + ver_len > data.len() {
return Err("truncated compiler version data".to_string());
}
let x86_compiler_version = String::from_utf8(data[pos..pos + ver_len].to_vec())
.map_err(|e| format!("invalid UTF-8 in compiler ver: {}", e))?;
pos += ver_len;
if pos >= data.len() {
return Err("truncated at module map flag".to_string());
}
let has_map = data[pos] == 1;
pos += 1;
let embedded_module_map = if has_map {
if pos + 8 > data.len() {
return Err("truncated module map size".to_string());
}
let map_len = u64::from_le_bytes([
data[pos],
data[pos + 1],
data[pos + 2],
data[pos + 3],
data[pos + 4],
data[pos + 5],
data[pos + 6],
data[pos + 7],
]) as usize;
pos += 8;
if pos + map_len > data.len() {
return Err("truncated module map data".to_string());
}
let map = String::from_utf8(data[pos..pos + map_len].to_vec())
.map_err(|e| format!("invalid UTF-8 in module map: {}", e))?;
pos += map_len;
Some(map)
} else {
None
};
if pos + 8 > data.len() {
return Err("truncated X86 flags".to_string());
}
let flags = u64::from_le_bytes([
data[pos],
data[pos + 1],
data[pos + 2],
data[pos + 3],
data[pos + 4],
data[pos + 5],
data[pos + 6],
data[pos + 7],
]);
pos += 8;
let stack_alignment =
u32::from_le_bytes([data[pos], data[pos + 1], data[pos + 2], data[pos + 3]]);
pos += 4;
let x86_flags = X86BMIFlags {
sse_enabled: (flags & (1 << 0)) != 0,
sse2_enabled: (flags & (1 << 1)) != 0,
avx_enabled: (flags & (1 << 2)) != 0,
avx2_enabled: (flags & (1 << 3)) != 0,
avx512_enabled: (flags & (1 << 4)) != 0,
is_64bit: (flags & (1 << 5)) != 0,
microsoft_abi: (flags & (1 << 6)) != 0,
rtti_enabled: (flags & (1 << 7)) != 0,
exceptions_enabled: (flags & (1 << 8)) != 0,
stack_alignment,
};
if pos + 32 > data.len() {
return Err("truncated BMI signature".to_string());
}
let mut signature = [0u8; 32];
signature.copy_from_slice(&data[pos..pos + 32]);
let result = X86BmiFile {
base,
x86_target_triple,
x86_compiler_version,
embedded_module_map,
x86_flags,
signature,
};
if self.verify_signatures {
let computed = self.compute_signature(&result);
if computed != signature {
return Err("BMI signature verification failed".to_string());
}
}
Ok(result)
}
pub fn compute_signature(&self, bmi: &X86BmiFile) -> [u8; 32] {
let mut hash = [0u8; 32];
let name_str = bmi.base.module_name.to_string();
for (i, byte) in name_str.bytes().enumerate() {
hash[i % 32] = hash[i % 32].wrapping_add(byte);
}
if self.signature_keys.include_source_hash {
for (i, byte) in bmi.base.ast_data.iter().enumerate() {
hash[i.wrapping_mul(7) % 32] = hash[i.wrapping_mul(7) % 32].wrapping_add(*byte);
}
}
if self.signature_keys.include_target {
for (i, byte) in bmi.x86_target_triple.bytes().enumerate() {
hash[(i + 8) % 32] = hash[(i + 8) % 32].wrapping_add(byte);
}
}
if self.signature_keys.include_dependency_hashes {
for dep in &bmi.base.dependencies {
let dep_str = dep.to_string();
for (i, byte) in dep_str.bytes().enumerate() {
hash[(i + 16) % 32] = hash[(i + 16) % 32].wrapping_add(byte);
}
}
}
if self.signature_keys.include_flags {
hash[24] = if bmi.x86_flags.is_64bit { 1 } else { 0 };
hash[25] = if bmi.x86_flags.sse2_enabled { 1 } else { 0 };
hash[26] = if bmi.x86_flags.avx_enabled { 1 } else { 0 };
hash[27] = if bmi.x86_flags.rtti_enabled { 1 } else { 0 };
hash[28] = if bmi.x86_flags.exceptions_enabled {
1
} else {
0
};
hash[29] = if bmi.x86_flags.microsoft_abi { 1 } else { 0 };
}
hash
}
pub fn check_version_compatibility(&self, bmi: &X86BmiFile) -> Result<(), String> {
if bmi.x86_compiler_version != self.compiler_version {
}
if self.signature_keys.include_target {
let bmi_triple = &bmi.x86_target_triple;
if !self.are_triples_compatible(bmi_triple, &self.target_triple) {
return Err(format!(
"BMI target triple '{}' is incompatible with current target '{}'",
bmi_triple, self.target_triple
));
}
}
Ok(())
}
fn are_triples_compatible(&self, a: &str, b: &str) -> bool {
let arch_a = a.split('-').next().unwrap_or("");
let arch_b = b.split('-').next().unwrap_or("");
arch_a == arch_b
}
pub fn operation_log(&self) -> &[X86BMIOperation] {
&self.operation_log
}
pub fn clear_log(&mut self) {
self.operation_log.clear();
}
pub fn validate_bmi_file(&self, path: &Path) -> Result<(), String> {
if !path.exists() {
return Err(format!("BMI file does not exist: {}", path.display()));
}
let metadata =
fs::metadata(path).map_err(|e| format!("failed to read BMI metadata: {}", e))?;
if metadata.len() < 16 {
return Err(format!("BMI file too small: {} bytes", metadata.len()));
}
Ok(())
}
pub fn remove_bmi(&mut self, path: &Path) -> Result<(), String> {
fs::remove_file(path).map_err(|e| format!("failed to remove BMI: {}", e))?;
self.operation_log.push(X86BMIOperation {
op: X86BMIOpKind::Remove,
module_name: path.to_string_lossy().to_string(),
file_size: 0,
success: true,
error: None,
});
Ok(())
}
}
impl Default for X86BMITransfer {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct X86ModuleCache {
pub cache_root: PathBuf,
pub max_size_bytes: u64,
pub max_versions_per_module: usize,
pub enabled: bool,
cache_index: HashMap<String, Vec<X86CacheEntry>>,
current_size_bytes: u64,
options_hash: u64,
x86_cache_config: X86CacheConfig,
}
#[derive(Debug, Clone)]
pub struct X86CacheEntry {
pub content_hash: String,
pub bmi_path: PathBuf,
pub size_bytes: u64,
pub mtime: u64,
pub flags_hash: u64,
pub module_version: Option<String>,
pub valid: bool,
}
#[derive(Debug, Clone)]
pub struct X86CacheConfig {
pub use_triple_subdir: bool,
pub target_triple: String,
pub use_lock_file: bool,
pub use_hard_links: bool,
pub verify_on_startup: bool,
pub gc_threshold: f64,
}
impl Default for X86CacheConfig {
fn default() -> Self {
Self {
use_triple_subdir: true,
target_triple: "x86_64-unknown-linux-gnu".to_string(),
use_lock_file: false,
use_hard_links: false,
verify_on_startup: false,
gc_threshold: 0.8,
}
}
}
impl X86ModuleCache {
pub fn new(options: &ClangOptions) -> Self {
let cache_root = PathBuf::from(X86_MODULE_CACHE_DIR);
let mut cache = Self {
cache_root,
max_size_bytes: X86_CACHE_MAX_SIZE_BYTES,
max_versions_per_module: X86_CACHE_MAX_VERSIONS,
enabled: true,
cache_index: HashMap::new(),
current_size_bytes: 0,
options_hash: Self::compute_options_hash(options),
x86_cache_config: X86CacheConfig::default(),
};
if cache.enabled {
let _ = fs::create_dir_all(&cache.cache_root);
}
let _ = cache.index_cache();
cache
}
fn compute_options_hash(options: &ClangOptions) -> u64 {
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
let mut hasher = DefaultHasher::new();
options.standard.as_str().hash(&mut hasher);
options.optimize.hash(&mut hasher);
options.debug_info.hash(&mut hasher);
options.pedantic.hash(&mut hasher);
options.target_triple.hash(&mut hasher);
for inc in &options.includes {
inc.hash(&mut hasher);
}
for (key, val) in &options.defines {
key.hash(&mut hasher);
val.hash(&mut hasher);
}
hasher.finish()
}
pub fn set_cache_root(&mut self, root: &Path) {
self.cache_root = root.to_path_buf();
if self.enabled {
let _ = fs::create_dir_all(&self.cache_root);
let _ = self.index_cache();
}
}
pub fn cache_subdir(&self) -> PathBuf {
if self.x86_cache_config.use_triple_subdir {
self.cache_root.join(&self.x86_cache_config.target_triple)
} else {
self.cache_root.clone()
}
}
fn index_cache(&mut self) -> Result<usize, String> {
if !self.enabled {
return Ok(0);
}
let subdir = self.cache_subdir();
if !subdir.exists() {
return Ok(0);
}
let mut count = 0;
self.cache_index.clear();
self.current_size_bytes = 0;
if let Ok(entries) = fs::read_dir(&subdir) {
for entry in entries.flatten() {
let path = entry.path();
if path.extension().map(|e| e == "pcm").unwrap_or(false) {
if let Ok(metadata) = entry.metadata() {
let size = metadata.len();
let mtime = metadata
.modified()
.map(|t| {
t.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_secs())
.unwrap_or(0)
})
.unwrap_or(0);
let stem = path.file_stem().unwrap_or_default().to_string_lossy();
let module_name = stem.to_string();
let cache_entry = X86CacheEntry {
content_hash: format!("{:x}", mtime.wrapping_mul(13)),
bmi_path: path.clone(),
size_bytes: size,
mtime,
flags_hash: self.options_hash,
module_version: None,
valid: true,
};
self.cache_index
.entry(module_name)
.or_default()
.push(cache_entry);
self.current_size_bytes += size;
count += 1;
}
}
}
}
Ok(count)
}
pub fn lookup(&self, module_name: &str) -> Option<PathBuf> {
if !self.enabled {
return None;
}
self.cache_index
.get(module_name)
.and_then(|entries| {
entries
.iter()
.filter(|e| e.valid && e.flags_hash == self.options_hash)
.max_by_key(|e| e.mtime)
})
.map(|e| e.bmi_path.clone())
}
pub fn lookup_by_hash(&self, module_name: &str, content_hash: &str) -> Option<PathBuf> {
if !self.enabled {
return None;
}
self.cache_index.get(module_name).and_then(|entries| {
entries
.iter()
.filter(|e| e.valid && e.content_hash == content_hash)
.max_by_key(|e| e.mtime)
.map(|e| e.bmi_path.clone())
})
}
pub fn store(
&mut self,
_unit: &X86CompilationUnit,
result: &X86CompilationResult,
) -> Result<(), String> {
if !self.enabled {
return Ok(());
}
let module_name = result.module_name.clone();
let subdir = self.cache_subdir();
fs::create_dir_all(&subdir).map_err(|e| format!("failed to create cache subdir: {}", e))?;
let bmi_path = if result.bmi_path.is_some() {
let cached_name = format!("{}-{}.pcm", module_name, result.module_name);
subdir.join(cached_name)
} else {
return Ok(()); };
if let Some(ref src_path) = result.bmi_path {
if src_path != &bmi_path {
if self.x86_cache_config.use_hard_links {
fs::hard_link(src_path, &bmi_path)
.or_else(|_| fs::copy(src_path, &bmi_path).map(|_| ()))
.map_err(|e| format!("failed to cache BMI: {}", e))?;
} else {
fs::copy(src_path, &bmi_path)
.map_err(|e| format!("failed to cache BMI: {}", e))?;
}
}
if let Ok(metadata) = fs::metadata(&bmi_path) {
let size = metadata.len();
let mtime = metadata
.modified()
.map(|t| {
t.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_secs())
.unwrap_or(0)
})
.unwrap_or(0);
let entry = X86CacheEntry {
content_hash: format!("{:x}", size.wrapping_mul(13)),
bmi_path: bmi_path.clone(),
size_bytes: size,
mtime,
flags_hash: self.options_hash,
module_version: None,
valid: true,
};
let module_entries = self.cache_index.entry(module_name).or_default();
module_entries.push(entry);
if module_entries.len() > self.max_versions_per_module {
module_entries.sort_by_key(|e| e.mtime);
module_entries.remove(0); }
self.current_size_bytes += size;
}
}
if self.current_size_bytes as f64
> self.max_size_bytes as f64 * self.x86_cache_config.gc_threshold
{
self.prune(0)?;
}
Ok(())
}
pub fn invalidate(&mut self, module_name: &str) -> usize {
if !self.enabled {
return 0;
}
let mut count = 0;
if let Some(entries) = self.cache_index.get_mut(module_name) {
for entry in entries.iter_mut() {
if entry.valid {
entry.valid = false;
count += 1;
}
}
}
count
}
pub fn invalidate_by_flags(&mut self, new_options: &ClangOptions) -> usize {
let new_hash = Self::compute_options_hash(new_options);
if new_hash == self.options_hash {
return 0;
}
self.options_hash = new_hash;
let mut count = 0;
for entries in self.cache_index.values_mut() {
for entry in entries.iter_mut() {
if entry.flags_hash != new_hash && entry.valid {
entry.valid = false;
count += 1;
}
}
}
count
}
pub fn prune(&mut self, target_bytes: u64) -> Result<usize, String> {
if !self.enabled {
return Ok(0);
}
let target = if target_bytes > 0 {
target_bytes
} else {
self.max_size_bytes
};
let mut removed = 0usize;
let mut to_remove = Vec::new();
for (name, entries) in &self.cache_index {
for (i, entry) in entries.iter().enumerate() {
if !entry.valid {
to_remove.push((name.clone(), i));
}
}
}
for (name, idx) in to_remove.iter().rev() {
if let Some(entries) = self.cache_index.get_mut(name) {
if *idx < entries.len() {
let entry = entries.remove(*idx);
if entry.bmi_path.exists() {
let _ = fs::remove_file(&entry.bmi_path);
}
self.current_size_bytes =
self.current_size_bytes.saturating_sub(entry.size_bytes);
removed += 1;
}
}
}
if self.current_size_bytes > target {
let mut all_entries: Vec<(String, usize, u64, PathBuf, u64)> = Vec::new();
for (name, entries) in &self.cache_index {
for (i, entry) in entries.iter().enumerate() {
all_entries.push((
name.clone(),
i,
entry.mtime,
entry.bmi_path.clone(),
entry.size_bytes,
));
}
}
all_entries.sort_by_key(|(_, _, mtime, _, _)| *mtime);
for (name, _idx, _mtime, path, size) in &all_entries {
if self.current_size_bytes <= target {
break;
}
if path.exists() {
let _ = fs::remove_file(path);
}
self.current_size_bytes = self.current_size_bytes.saturating_sub(*size);
removed += 1;
if let Some(entries) = self.cache_index.get_mut(name) {
entries.retain(|e| &e.bmi_path != path);
}
}
}
Ok(removed)
}
pub fn clear(&mut self) -> Result<usize, String> {
if !self.enabled {
return Ok(0);
}
let subdir = self.cache_subdir();
let mut removed = 0usize;
if subdir.exists() {
for entry in fs::read_dir(&subdir)
.map_err(|e| format!("read_dir: {}", e))?
.flatten()
{
if entry.file_type().map(|t| t.is_file()).unwrap_or(false) {
if fs::remove_file(entry.path()).is_ok() {
removed += 1;
}
}
}
}
self.cache_index.clear();
self.current_size_bytes = 0;
Ok(removed)
}
pub fn current_size_bytes(&self) -> u64 {
self.current_size_bytes
}
pub fn entry_count(&self) -> usize {
self.cache_index.values().map(|v| v.len()).sum()
}
pub fn valid_entry_count(&self) -> usize {
self.cache_index
.values()
.map(|v| v.iter().filter(|e| e.valid).count())
.sum()
}
pub fn verify_cache(&self) -> Vec<String> {
let mut errors = Vec::new();
for entries in self.cache_index.values() {
for entry in entries {
if !entry.bmi_path.exists() {
errors.push(format!("cached BMI missing: {}", entry.bmi_path.display()));
}
if entry.valid {
if let Ok(meta) = fs::metadata(&entry.bmi_path) {
if meta.len() != entry.size_bytes {
errors.push(format!(
"cached BMI size mismatch: {} (expected {}, got {})",
entry.bmi_path.display(),
entry.size_bytes,
meta.len()
));
}
}
}
}
}
errors
}
}
#[derive(Debug, Clone)]
pub struct X86ModuleOwnership {
pub base: ModuleOwnership,
pub current_module: Option<String>,
owner_map: HashMap<usize, (String, bool)>,
reachability_map: HashMap<usize, HashSet<String>>,
export_map: HashMap<String, HashSet<usize>>,
using_declarations: Vec<(String, String)>,
visible_names: HashMap<String, HashSet<String>>,
reachable_names: HashMap<String, HashSet<String>>,
tu_module: Option<String>,
in_global_fragment: bool,
in_private_fragment: bool,
}
impl X86ModuleOwnership {
pub fn new() -> Self {
Self {
base: ModuleOwnership::new(),
current_module: None,
owner_map: HashMap::new(),
reachability_map: HashMap::new(),
export_map: HashMap::new(),
using_declarations: Vec::new(),
visible_names: HashMap::new(),
reachable_names: HashMap::new(),
tu_module: None,
in_global_fragment: false,
in_private_fragment: false,
}
}
pub fn register_module(&mut self, name: ModuleName, _kind: X86CompilationUnitKind) {
let name_str = name.to_string();
self.current_module = Some(name_str.clone());
self.tu_module = Some(name_str.clone());
self.visible_names.entry(name_str.clone()).or_default();
self.reachable_names.entry(name_str.clone()).or_default();
}
pub fn enter_global_fragment(&mut self) {
self.in_global_fragment = true;
}
pub fn leave_global_fragment(&mut self) {
self.in_global_fragment = false;
}
pub fn enter_private_fragment(&mut self) {
self.in_private_fragment = true;
}
pub fn leave_private_fragment(&mut self) {
self.in_private_fragment = false;
}
pub fn record_decl_ownership(&mut self, decl_id: usize, decl_name: &str) {
if let Some(ref module) = self.current_module {
let is_exported = !self.in_global_fragment && !self.in_private_fragment;
self.owner_map
.insert(decl_id, (module.clone(), is_exported));
if is_exported {
self.visible_names
.entry(module.clone())
.or_default()
.insert(decl_name.to_string());
}
self.reachable_names
.entry(module.clone())
.or_default()
.insert(decl_name.to_string());
}
}
pub fn mark_exported(&mut self, decl_id: usize) {
if let Some(ref module) = self.current_module {
self.export_map
.entry(module.clone())
.or_default()
.insert(decl_id);
if let Some((_, exported)) = self.owner_map.get_mut(&decl_id) {
*exported = true;
}
if let Some(ref set) = self.owner_map.get(&decl_id) {
let module_name = set.0.clone();
if let Some(reachable) = self.reachability_map.get_mut(&decl_id) {
reachable.insert(module_name);
}
}
}
}
pub fn mark_reachable_from(&mut self, decl_id: usize, from_module: &str) {
self.reachability_map
.entry(decl_id)
.or_default()
.insert(from_module.to_string());
}
pub fn is_reachable_from(&self, decl_id: usize, from_module: &str) -> bool {
self.reachability_map
.get(&decl_id)
.map(|set| set.contains(from_module))
.unwrap_or(false)
}
pub fn owner_of(&self, decl_id: usize) -> Option<&str> {
self.owner_map
.get(&decl_id)
.map(|(module, _)| module.as_str())
}
pub fn is_exported(&self, decl_id: usize) -> bool {
self.owner_map
.get(&decl_id)
.map(|(_, exported)| *exported)
.unwrap_or(false)
}
pub fn is_name_visible(&self, name: &str, from_module: &str) -> bool {
if self.in_global_fragment {
return true;
}
if let Some(visible) = self.visible_names.get(from_module) {
if visible.contains(name) {
return true;
}
}
if let Some(reachable) = self.reachable_names.get(from_module) {
if reachable.contains(name) {
return true;
}
}
false
}
pub fn register_visible_name(&mut self, module: &str, name: &str) {
self.visible_names
.entry(module.to_string())
.or_default()
.insert(name.to_string());
}
pub fn register_reachable_name(&mut self, module: &str, name: &str) {
self.reachable_names
.entry(module.to_string())
.or_default()
.insert(name.to_string());
}
pub fn process_export_decl(&mut self, exported_names: &[String]) {
for name in exported_names {
if let Some(ref module) = self.current_module {
self.visible_names
.entry(module.clone())
.or_default()
.insert(name.clone());
}
}
}
pub fn register_using_decl(&mut self, target_name: &str, source_module: &str) {
self.using_declarations
.push((target_name.to_string(), source_module.to_string()));
if let Some(ref current) = self.current_module {
self.visible_names
.entry(current.clone())
.or_default()
.insert(target_name.to_string());
self.reachable_names
.entry(current.clone())
.or_default()
.insert(target_name.to_string());
}
}
pub fn using_declarations(&self) -> &[(String, String)] {
&self.using_declarations
}
pub fn compute_visible_names(&self, module: &str) -> HashSet<String> {
let mut visible = HashSet::new();
if let Some(names) = self.visible_names.get(module) {
visible.extend(names.iter().cloned());
}
if let Some(names) = self.reachable_names.get(module) {
visible.extend(names.iter().cloned());
}
for (name, _source) in &self.using_declarations {
visible.insert(name.clone());
}
visible
}
pub fn compute_reachable_names(
&self,
module: &str,
dependency: &X86ModuleDependency,
) -> HashSet<String> {
let mut reachable = HashSet::new();
let mut visited = HashSet::new();
let mut stack = vec![module.to_string()];
while let Some(current) = stack.pop() {
if !visited.insert(current.clone()) {
continue;
}
if let Some(names) = self.visible_names.get(¤t) {
reachable.extend(names.iter().cloned());
}
for dep in dependency.direct_dependencies(¤t) {
if !visited.contains(&dep) {
stack.push(dep);
}
}
}
reachable
}
pub fn validate(&self) -> Vec<String> {
let mut errors = Vec::new();
for (module, decls) in &self.export_map {
if !self.visible_names.contains_key(module)
&& !self.reachable_names.contains_key(module)
{
errors.push(format!(
"exported declarations for module '{}' but module not registered",
module
));
}
for decl_id in decls {
if let Some((owner, _)) = self.owner_map.get(decl_id) {
if owner != module {
errors.push(format!(
"declaration {} is exported from '{}' but owned by '{}'",
decl_id, module, owner
));
}
}
}
}
errors
}
}
impl Default for X86ModuleOwnership {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct X86ModuleLinkage {
pub enabled: bool,
pub strong_linkage_by_default: bool,
pub emit_initializers: bool,
pub emit_finalizers: bool,
linkage_overrides: HashMap<String, X86LinkageOverride>,
symbol_linkage: HashMap<usize, X86SymbolLinkage>,
initializer_registry: Vec<X86ModuleInit>,
finalizer_registry: Vec<X86ModuleFini>,
options: ClangOptions,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86ModuleLinkageKind {
Strong,
Weak,
WeakODR,
Internal,
LinkOnceODR,
AvailableExternally,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86SymbolVisibility {
Default,
Hidden,
Protected,
Internal,
}
#[derive(Debug, Clone)]
pub struct X86LinkageOverride {
pub module_name: String,
pub linkage: X86ModuleLinkageKind,
pub visibility: X86SymbolVisibility,
pub force_inline: bool,
}
#[derive(Debug, Clone)]
pub struct X86SymbolLinkage {
pub symbol_name: String,
pub linkage: X86ModuleLinkageKind,
pub visibility: X86SymbolVisibility,
pub owning_module: String,
pub is_definition: bool,
}
#[derive(Debug, Clone)]
pub struct X86ModuleInit {
pub module_name: String,
pub init_function: String,
pub priority: u32,
pub before_main: bool,
}
#[derive(Debug, Clone)]
pub struct X86ModuleFini {
pub module_name: String,
pub fini_function: String,
pub priority: u32,
pub at_exit: bool,
}
impl X86ModuleLinkage {
pub fn new(options: &ClangOptions) -> Self {
Self {
enabled: true,
strong_linkage_by_default: false,
emit_initializers: true,
emit_finalizers: false,
linkage_overrides: HashMap::new(),
symbol_linkage: HashMap::new(),
initializer_registry: Vec::new(),
finalizer_registry: Vec::new(),
options: options.clone(),
}
}
pub fn register_module(
&mut self,
name: &ModuleName,
kind: X86CompilationUnitKind,
_options: &ClangOptions,
) {
let _name_str = name.to_string();
let _is_interface = matches!(
kind,
X86CompilationUnitKind::ModuleInterface
| X86CompilationUnitKind::ModulePartitionInterface
);
}
pub fn determine_linkage(
&self,
symbol_name: &str,
module_name: &str,
is_definition: bool,
is_exported: bool,
) -> X86SymbolLinkage {
if let Some(ov) = self.linkage_overrides.get(module_name) {
return X86SymbolLinkage {
symbol_name: symbol_name.to_string(),
linkage: ov.linkage,
visibility: ov.visibility,
owning_module: module_name.to_string(),
is_definition,
};
}
let linkage = if is_exported {
if self.strong_linkage_by_default {
X86ModuleLinkageKind::Strong
} else {
X86ModuleLinkageKind::WeakODR
}
} else {
X86ModuleLinkageKind::Internal
};
let visibility = if is_exported {
X86SymbolVisibility::Default
} else {
X86SymbolVisibility::Hidden
};
X86SymbolLinkage {
symbol_name: symbol_name.to_string(),
linkage,
visibility,
owning_module: module_name.to_string(),
is_definition,
}
}
pub fn assign_linkage(&mut self, decl_id: usize, linkage: X86SymbolLinkage) {
self.symbol_linkage.insert(decl_id, linkage);
}
pub fn get_linkage(&self, decl_id: usize) -> Option<&X86SymbolLinkage> {
self.symbol_linkage.get(&decl_id)
}
pub fn register_initializer(&mut self, module_name: &str, init_function: &str, priority: u32) {
self.initializer_registry.push(X86ModuleInit {
module_name: module_name.to_string(),
init_function: init_function.to_string(),
priority,
before_main: true,
});
}
pub fn register_finalizer(&mut self, module_name: &str, fini_function: &str, priority: u32) {
self.finalizer_registry.push(X86ModuleFini {
module_name: module_name.to_string(),
fini_function: fini_function.to_string(),
priority,
at_exit: true,
});
}
pub fn generate_initializer_ir(&self) -> String {
let mut ir = String::new();
ir.push_str("; Module initializers\n");
for init in &self.initializer_registry {
ir.push_str(&format!(
"@llvm.global_ctors = appending global [1 x {{ i32, void ()*, i8* }}] [\n"
));
ir.push_str(&format!(
" {{ i32, void ()*, i8* }} {{ i32 {}, void ()* @{}, i8* null }}\n",
init.priority, init.init_function
));
ir.push_str("]\n");
}
ir
}
pub fn generate_finalizer_ir(&self) -> String {
let mut ir = String::new();
ir.push_str("; Module finalizers\n");
for fini in &self.finalizer_registry {
ir.push_str(&format!(
"@llvm.global_dtors = appending global [1 x {{ i32, void ()*, i8* }}] [\n"
));
ir.push_str(&format!(
" {{ i32, void ()*, i8* }} {{ i32 {}, void ()* @{}, i8* null }}\n",
fini.priority, fini.fini_function
));
ir.push_str("]\n");
}
ir
}
pub fn set_linkage_override(&mut self, module_name: &str, override_config: X86LinkageOverride) {
self.linkage_overrides
.insert(module_name.to_string(), override_config);
}
pub fn remove_linkage_override(&mut self, module_name: &str) {
self.linkage_overrides.remove(module_name);
}
pub fn linkage_to_ir_string(linkage: X86ModuleLinkageKind) -> &'static str {
match linkage {
X86ModuleLinkageKind::Strong => "external",
X86ModuleLinkageKind::Weak => "weak",
X86ModuleLinkageKind::WeakODR => "weak_odr",
X86ModuleLinkageKind::Internal => "internal",
X86ModuleLinkageKind::LinkOnceODR => "linkonce_odr",
X86ModuleLinkageKind::AvailableExternally => "available_externally",
}
}
pub fn visibility_to_ir_string(visibility: X86SymbolVisibility) -> &'static str {
match visibility {
X86SymbolVisibility::Default => "default",
X86SymbolVisibility::Hidden => "hidden",
X86SymbolVisibility::Protected => "protected",
X86SymbolVisibility::Internal => "internal",
}
}
pub fn generate_linkage_metadata(&self) -> String {
let mut ir = String::new();
ir.push_str("; Module linkage metadata\n");
for (decl_id, linkage) in &self.symbol_linkage {
ir.push_str(&format!(
"!module.linkage.{} = !{{!\"{}\", !\"{}\", !\"{}\"}}\n",
decl_id,
linkage.symbol_name,
Self::linkage_to_ir_string(linkage.linkage),
Self::visibility_to_ir_string(linkage.visibility),
));
}
ir
}
pub fn validate(&self) -> Vec<String> {
let mut errors = Vec::new();
let mut seen_inits: HashMap<String, Vec<u32>> = HashMap::new();
for init in &self.initializer_registry {
seen_inits
.entry(init.module_name.clone())
.or_default()
.push(init.priority);
}
for (module, priorities) in &seen_inits {
let len_before = priorities.len();
let mut deduped = priorities.clone();
deduped.sort();
deduped.dedup();
if len_before != deduped.len() {
errors.push(format!(
"module '{}' has duplicate initializer priorities",
module
));
}
}
errors
}
}
#[derive(Debug, Clone)]
pub struct X86ModuleImport {
active_imports: Vec<ModuleImport>,
resolved_imports: HashMap<String, PathBuf>,
re_exports: HashMap<String, HashSet<String>>,
conditional_imports: Vec<(String, String)>,
processed_header_units: HashSet<String>,
import_errors: Vec<String>,
allow_unresolved: bool,
system_include_paths: Vec<PathBuf>,
}
impl X86ModuleImport {
pub fn new() -> Self {
Self {
active_imports: Vec::new(),
resolved_imports: HashMap::new(),
re_exports: HashMap::new(),
conditional_imports: Vec::new(),
processed_header_units: HashSet::new(),
import_errors: Vec::new(),
allow_unresolved: false,
system_include_paths: Vec::new(),
}
}
pub fn set_include_paths(&mut self, paths: Vec<PathBuf>) {
self.system_include_paths = paths;
}
pub fn set_allow_unresolved(&mut self, allow: bool) {
self.allow_unresolved = allow;
}
pub fn parse_import(&self, source: &str) -> Option<ModuleImport> {
crate::clang::cpp_modules::parse_import_decl(source)
}
pub fn resolve_import(
&mut self,
import: &ModuleImport,
module_map: &X86ModuleMap,
cache: &X86ModuleCache,
) -> Result<PathBuf, String> {
if import.is_header_unit {
if let Some(ref header) = import.header_name {
return self.resolve_header_unit(header, cache);
}
return Err("header unit import has no header name".to_string());
}
let target_name = import.name.to_string();
if let Some(path) = self.resolved_imports.get(&target_name) {
return Ok(path.clone());
}
let resolved_name = module_map
.resolve_name(&target_name)
.ok_or_else(|| format!("module '{}' not found in module map", target_name))?;
if let Some(bmi_path) = cache.lookup(&resolved_name) {
self.resolved_imports
.insert(target_name.clone(), bmi_path.clone());
self.active_imports.push(import.clone());
Ok(bmi_path)
} else if self.allow_unresolved {
Ok(PathBuf::new())
} else {
Err(format!(
"BMI for module '{}' not found in cache",
resolved_name
))
}
}
fn resolve_header_unit(
&mut self,
header: &str,
cache: &X86ModuleCache,
) -> Result<PathBuf, String> {
let cache_key = format!("<{}>", header);
if let Some(path) = self.resolved_imports.get(&cache_key) {
return Ok(path.clone());
}
let module_name = header.replace(['/', '\\', '.', '<', '>'], "_");
if let Some(bmi_path) = cache.lookup(&module_name) {
self.resolved_imports
.insert(cache_key.clone(), bmi_path.clone());
self.processed_header_units.insert(header.to_string());
return Ok(bmi_path);
}
for path in &self.system_include_paths {
let full_path = path.join(header);
if full_path.exists() {
self.processed_header_units.insert(header.to_string());
return Ok(full_path);
}
}
if self.allow_unresolved {
Ok(PathBuf::new())
} else {
Err(format!("header '{}' not found in include paths", header))
}
}
pub fn handle_export_import(&mut self, import: &ModuleImport, current_module: &str) {
if import.is_exported {
let target_name = import.name.to_string();
self.re_exports
.entry(current_module.to_string())
.or_default()
.insert(target_name);
}
self.active_imports.push(import.clone());
}
pub fn register_conditional_import(&mut self, condition: &str, target_module: &str) {
self.conditional_imports
.push((condition.to_string(), target_module.to_string()));
}
pub fn evaluate_conditional_imports(
&mut self,
true_conditions: &HashSet<String>,
) -> Vec<String> {
let mut resolved = Vec::new();
for (condition, target) in &self.conditional_imports {
if true_conditions.contains(condition) {
resolved.push(target.clone());
}
}
resolved
}
pub fn is_imported(&self, module_name: &str) -> bool {
self.active_imports
.iter()
.any(|imp| imp.name.to_string() == module_name)
}
pub fn is_re_exported_by(&self, module: &str, parent: &str) -> bool {
self.re_exports
.get(parent)
.map(|set| set.contains(module))
.unwrap_or(false)
}
pub fn re_exports_of(&self, module: &str) -> Vec<&str> {
self.re_exports
.get(module)
.map(|set| set.iter().map(|s| s.as_str()).collect())
.unwrap_or_default()
}
pub fn is_import_resolved(&self, module_name: &str) -> bool {
self.resolved_imports.contains_key(module_name)
}
pub fn resolved_bmi_path(&self, module_name: &str) -> Option<&PathBuf> {
self.resolved_imports.get(module_name)
}
pub fn active_imports(&self) -> &[ModuleImport] {
&self.active_imports
}
pub fn resolved_imports(&self) -> &HashMap<String, PathBuf> {
&self.resolved_imports
}
pub fn clear(&mut self) {
self.active_imports.clear();
self.resolved_imports.clear();
self.re_exports.clear();
self.conditional_imports.clear();
self.import_errors.clear();
}
pub fn errors(&self) -> &[String] {
&self.import_errors
}
pub fn generate_import_metadata(&self) -> String {
let mut ir = String::new();
ir.push_str("; Module import metadata\n");
for import in &self.active_imports {
if import.is_header_unit {
if let Some(ref header) = import.header_name {
ir.push_str(&format!("!module.import.header = !{{!\"{}\"}}\n", header));
}
} else {
let name = import.name.to_string();
ir.push_str(&format!(
"!module.import.{} = !{{!\"{}\", i1 {}}}\n",
name,
name,
if import.is_exported { "true" } else { "false" }
));
}
}
ir
}
}
impl Default for X86ModuleImport {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct X86ModuleStandardLibrary {
pub modules: Vec<X86StdModule>,
pub module_map: X86ModuleMap,
pub enabled: bool,
pub cpp_standard: CppStandard,
pub experimental: bool,
pub use_installed_library: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CppStandard {
Cpp17,
Cpp20,
Cpp23,
Cpp26,
}
impl CppStandard {
pub fn as_str(&self) -> &'static str {
match self {
CppStandard::Cpp17 => "c++17",
CppStandard::Cpp20 => "c++20",
CppStandard::Cpp23 => "c++23",
CppStandard::Cpp26 => "c++26",
}
}
pub fn supports_modules(&self) -> bool {
matches!(
self,
CppStandard::Cpp20 | CppStandard::Cpp23 | CppStandard::Cpp26
)
}
}
#[derive(Debug, Clone)]
pub struct X86StdModule {
pub name: String,
pub headers: Vec<String>,
pub dependencies: Vec<String>,
pub is_primary: bool,
pub min_standard: CppStandard,
pub experimental: bool,
pub x86_config: Vec<String>,
}
impl X86StdModule {
pub fn new(name: &str) -> Self {
Self {
name: name.to_string(),
headers: Vec::new(),
dependencies: Vec::new(),
is_primary: false,
min_standard: CppStandard::Cpp20,
experimental: false,
x86_config: Vec::new(),
}
}
pub fn add_header(&mut self, header: &str) {
self.headers.push(header.to_string());
}
pub fn add_dependency(&mut self, dep: &str) {
self.dependencies.push(dep.to_string());
}
}
impl X86ModuleStandardLibrary {
pub fn new() -> Self {
let mut sl = Self {
modules: Vec::new(),
module_map: X86ModuleMap::new(),
enabled: true,
cpp_standard: CppStandard::Cpp20,
experimental: false,
use_installed_library: false,
};
sl.initialize_std_modules();
sl.build_module_map();
sl
}
fn initialize_std_modules(&mut self) {
self.modules.push(X86StdModule {
name: "std".to_string(),
headers: vec![
"<algorithm>".into(),
"<any>".into(),
"<array>".into(),
"<atomic>".into(),
"<barrier>".into(),
"<bit>".into(),
"<bitset>".into(),
"<charconv>".into(),
"<chrono>".into(),
"<compare>".into(),
"<complex>".into(),
"<concepts>".into(),
"<condition_variable>".into(),
"<coroutine>".into(),
"<deque>".into(),
"<exception>".into(),
"<execution>".into(),
"<expected>".into(),
"<filesystem>".into(),
"<format>".into(),
"<forward_list>".into(),
"<fstream>".into(),
"<functional>".into(),
"<future>".into(),
"<initializer_list>".into(),
"<iomanip>".into(),
"<ios>".into(),
"<iosfwd>".into(),
"<iostream>".into(),
"<istream>".into(),
"<iterator>".into(),
"<latch>".into(),
"<limits>".into(),
"<list>".into(),
"<locale>".into(),
"<map>".into(),
"<memory>".into(),
"<memory_resource>".into(),
"<mutex>".into(),
"<new>".into(),
"<numbers>".into(),
"<numeric>".into(),
"<optional>".into(),
"<ostream>".into(),
"<queue>".into(),
"<random>".into(),
"<ranges>".into(),
"<ratio>".into(),
"<regex>".into(),
"<scoped_allocator>".into(),
"<semaphore>".into(),
"<set>".into(),
"<shared_mutex>".into(),
"<source_location>".into(),
"<span>".into(),
"<spanstream>".into(),
"<sstream>".into(),
"<stack>".into(),
"<stacktrace>".into(),
"<stdexcept>".into(),
"<stop_token>".into(),
"<streambuf>".into(),
"<string>".into(),
"<string_view>".into(),
"<strstream>".into(),
"<syncstream>".into(),
"<system_error>".into(),
"<thread>".into(),
"<tuple>".into(),
"<type_traits>".into(),
"<typeindex>".into(),
"<typeinfo>".into(),
"<unordered_map>".into(),
"<unordered_set>".into(),
"<utility>".into(),
"<valarray>".into(),
"<variant>".into(),
"<vector>".into(),
"<version>".into(),
],
dependencies: vec![
"std.core".into(),
"std.io".into(),
"std.filesystem".into(),
"std.threading".into(),
"std.memory".into(),
"std.ranges".into(),
],
is_primary: true,
min_standard: CppStandard::Cpp20,
experimental: false,
x86_config: vec!["x86_64".into(), "i386".into()],
});
self.modules.push(X86StdModule {
name: "std.core".to_string(),
headers: vec![
"<concepts>".into(),
"<coroutine>".into(),
"<compare>".into(),
"<initializer_list>".into(),
"<new>".into(),
"<source_location>".into(),
"<type_traits>".into(),
"<typeinfo>".into(),
"<version>".into(),
"<cstddef>".into(),
"<cstdint>".into(),
"<cstdlib>".into(),
"<cstring>".into(),
"<ctime>".into(),
],
dependencies: vec![],
is_primary: false,
min_standard: CppStandard::Cpp20,
experimental: false,
x86_config: vec!["x86_64".into(), "i386".into()],
});
self.modules.push(X86StdModule {
name: "std.io".to_string(),
headers: vec![
"<ios>".into(),
"<iosfwd>".into(),
"<iostream>".into(),
"<istream>".into(),
"<ostream>".into(),
"<fstream>".into(),
"<sstream>".into(),
"<iomanip>".into(),
"<streambuf>".into(),
"<syncstream>".into(),
"<spanstream>".into(),
],
dependencies: vec!["std.core".into(), "std.memory".into()],
is_primary: false,
min_standard: CppStandard::Cpp20,
experimental: false,
x86_config: vec!["x86_64".into(), "i386".into()],
});
self.modules.push(X86StdModule {
name: "std.filesystem".to_string(),
headers: vec!["<filesystem>".into()],
dependencies: vec!["std.core".into(), "std.io".into()],
is_primary: false,
min_standard: CppStandard::Cpp20,
experimental: false,
x86_config: vec!["x86_64".into(), "i386".into()],
});
self.modules.push(X86StdModule {
name: "std.threading".to_string(),
headers: vec![
"<atomic>".into(),
"<barrier>".into(),
"<condition_variable>".into(),
"<future>".into(),
"<latch>".into(),
"<mutex>".into(),
"<semaphore>".into(),
"<shared_mutex>".into(),
"<stop_token>".into(),
"<thread>".into(),
],
dependencies: vec!["std.core".into()],
is_primary: false,
min_standard: CppStandard::Cpp20,
experimental: false,
x86_config: vec!["x86_64".into(), "i386".into()],
});
self.modules.push(X86StdModule {
name: "std.memory".to_string(),
headers: vec![
"<memory>".into(),
"<memory_resource>".into(),
"<scoped_allocator>".into(),
],
dependencies: vec!["std.core".into()],
is_primary: false,
min_standard: CppStandard::Cpp20,
experimental: false,
x86_config: vec!["x86_64".into(), "i386".into()],
});
self.modules.push(X86StdModule {
name: "std.ranges".to_string(),
headers: vec![
"<ranges>".into(),
"<span>".into(),
"<algorithm>".into(),
"<numeric>".into(),
"<iterator>".into(),
],
dependencies: vec!["std.core".into(), "std.concepts".into()],
is_primary: false,
min_standard: CppStandard::Cpp20,
experimental: false,
x86_config: vec!["x86_64".into(), "i386".into()],
});
self.modules.push(X86StdModule {
name: "std.concepts".to_string(),
headers: vec!["<concepts>".into()],
dependencies: vec!["std.core".into()],
is_primary: false,
min_standard: CppStandard::Cpp20,
experimental: false,
x86_config: vec!["x86_64".into(), "i386".into()],
});
self.modules.push(X86StdModule {
name: "std.containers".to_string(),
headers: vec![
"<array>".into(),
"<deque>".into(),
"<forward_list>".into(),
"<list>".into(),
"<map>".into(),
"<queue>".into(),
"<set>".into(),
"<stack>".into(),
"<unordered_map>".into(),
"<unordered_set>".into(),
"<vector>".into(),
],
dependencies: vec!["std.core".into(), "std.memory".into()],
is_primary: false,
min_standard: CppStandard::Cpp20,
experimental: false,
x86_config: vec!["x86_64".into(), "i386".into()],
});
self.modules.push(X86StdModule {
name: "std.strings".to_string(),
headers: vec![
"<string>".into(),
"<string_view>".into(),
"<charconv>".into(),
"<format>".into(),
"<cctype>".into(),
"<cwchar>".into(),
],
dependencies: vec!["std.core".into(), "std.memory".into()],
is_primary: false,
min_standard: CppStandard::Cpp20,
experimental: false,
x86_config: vec!["x86_64".into(), "i386".into()],
});
self.modules.push(X86StdModule {
name: "std.numerics".to_string(),
headers: vec![
"<bit>".into(),
"<complex>".into(),
"<numbers>".into(),
"<numeric>".into(),
"<random>".into(),
"<ratio>".into(),
"<valarray>".into(),
"<cmath>".into(),
],
dependencies: vec!["std.core".into()],
is_primary: false,
min_standard: CppStandard::Cpp20,
experimental: false,
x86_config: vec!["x86_64".into(), "i386".into()],
});
if self.cpp_standard == CppStandard::Cpp23 || self.experimental {
self.modules.push(X86StdModule {
name: "std.expected".to_string(),
headers: vec!["<expected>".into()],
dependencies: vec!["std.core".into()],
is_primary: false,
min_standard: CppStandard::Cpp23,
experimental: false,
x86_config: vec!["x86_64".into()],
});
self.modules.push(X86StdModule {
name: "std.stacktrace".to_string(),
headers: vec!["<stacktrace>".into()],
dependencies: vec!["std.core".into(), "std.io".into()],
is_primary: false,
min_standard: CppStandard::Cpp23,
experimental: false,
x86_config: vec!["x86_64".into()],
});
}
}
fn build_module_map(&mut self) {
self.module_map = X86ModuleMap::new();
for module in &self.modules {
let mut entry = X86ModuleMapEntry::new(&module.name, Path::new("."));
entry.headers = module.headers.clone();
entry.exports = module.dependencies.clone();
if module.is_primary {
entry.is_explicit = false;
}
if module.x86_config.contains(&"x86_64".to_string()) {
entry.x86_config_flags.push("x86_64".into());
}
if module.x86_config.contains(&"i386".to_string()) {
entry.x86_config_flags.push("i386".into());
}
self.module_map
.register_submodule(&module.name, &module.name);
}
}
pub fn module_names(&self) -> Vec<&str> {
self.modules.iter().map(|m| m.name.as_str()).collect()
}
pub fn is_std_module(&self, name: &str) -> bool {
self.modules.iter().any(|m| m.name == name)
}
pub fn get_std_module(&self, name: &str) -> Option<&X86StdModule> {
self.modules.iter().find(|m| m.name == name)
}
pub fn generate_module_map(&self) -> String {
let mut out = String::new();
out.push_str("// C++ Standard Library Module Map — Auto-generated\n");
out.push_str("// Target: X86 / X86-64\n");
out.push_str(&format!(
"// C++ Standard: {}\n",
self.cpp_standard.as_str()
));
out.push_str("//\n");
out.push_str("// This file defines the standard library module interfaces\n");
out.push_str("// for use with `import std;` and sub-modules.\n\n");
for module in &self.modules {
out.push_str(&format!("module {} {{\n", module.name));
for h in &module.headers {
out.push_str(&format!(" header \"{}\"\n", h.trim_matches(['<', '>'])));
}
for dep in &module.dependencies {
out.push_str(&format!(" export {}\n", dep));
}
if module.is_primary {
out.push_str(" export *\n");
}
out.push_str("}\n\n");
}
out
}
pub fn generate_ir_metadata(&self) -> String {
let mut ir = String::new();
ir.push_str("; Standard Library Module Metadata\n");
for module in &self.modules {
ir.push_str(&format!(
"!std.module.{} = !{{!\"{}\", i1 true, i32 {}}}\n",
module.name,
module.name,
module.headers.len()
));
}
ir
}
pub fn set_cpp_standard(&mut self, standard: CppStandard) {
self.cpp_standard = standard;
self.modules.clear();
self.initialize_std_modules();
self.build_module_map();
}
}
impl Default for X86ModuleStandardLibrary {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct X86ModuleFormat {
pub format_version: u32,
pub compressed: bool,
pub compression_algorithm: String,
pub has_lookup_table: bool,
pub has_source_map: bool,
pub has_embedded_module_map: bool,
pub target_triple: String,
pub header_size: usize,
}
impl X86ModuleFormat {
pub fn new() -> Self {
Self {
format_version: X86_BMI_EXTENDED_VERSION,
compressed: false,
compression_algorithm: "none".to_string(),
has_lookup_table: true,
has_source_map: true,
has_embedded_module_map: false,
target_triple: "x86_64-unknown-linux-gnu".to_string(),
header_size: 64,
}
}
pub fn with_compression(mut self, algorithm: &str) -> Self {
self.compressed = true;
self.compression_algorithm = algorithm.to_string();
self
}
pub fn is_compatible_with(&self, other: &X86ModuleFormat) -> bool {
self.format_version == other.format_version && self.target_triple == other.target_triple
}
pub fn min_supported_version() -> u32 {
1
}
pub fn max_supported_version() -> u32 {
X86_BMI_EXTENDED_VERSION
}
pub fn is_version_supported(version: u32) -> bool {
version >= Self::min_supported_version() && version <= Self::max_supported_version()
}
}
impl Default for X86ModuleFormat {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct X86ModuleBuildSystem {
pub build_dir: PathBuf,
pub depfile_format: String,
pub generate_depfiles: bool,
pub use_response_files: bool,
pub job_server_active: bool,
pub max_jobs: usize,
}
impl X86ModuleBuildSystem {
pub fn new(build_dir: &Path) -> Self {
Self {
build_dir: build_dir.to_path_buf(),
depfile_format: "make".to_string(),
generate_depfiles: true,
use_response_files: false,
job_server_active: false,
max_jobs: 1,
}
}
pub fn generate_depfile(
&self,
output_path: &Path,
inputs: &[PathBuf],
dependencies: &[String],
) -> String {
let mut depfile = String::new();
depfile.push_str(&format!("{}:", output_path.display()));
for input in inputs {
depfile.push_str(&format!(" \\\n {}", input.display()));
}
for dep in dependencies {
depfile.push_str(&format!(" \\\n {}.pcm", dep));
}
depfile.push('\n');
for dep in dependencies {
depfile.push_str(&format!("\n{}.pcm:\n", dep));
}
depfile
}
pub fn generate_ninja_rule(&self) -> String {
let mut ninja = String::new();
ninja.push_str("# Ninja rules for C++20 module compilation\n");
ninja.push_str("rule cxx_module_interface\n");
ninja.push_str(
" command = clang++ -std=c++20 -c $in -Xclang -emit-module-interface -o $out\n",
);
ninja.push_str(" description = Compiling module interface $out\n");
ninja.push_str(" depfile = $out.d\n deps = gcc\n\n");
ninja.push_str("rule cxx_module_implementation\n");
ninja.push_str(" command = clang++ -std=c++20 -c $in -fmodule-file=$module_bmi -o $out\n");
ninja.push_str(" description = Compiling module implementation $out\n");
ninja
}
pub fn generate_cmake_snippet(&self, module_name: &str, source_file: &str) -> String {
format!(
"# CMake target for module {module}\ntarget_sources(${{PROJECT_NAME}} PRIVATE\n FILE_SET CXX_MODULES\n FILES {source}\n BASE_DIRS ${{CMAKE_CURRENT_SOURCE_DIR}}\n)\n",
module = module_name, source = source_file
)
}
pub fn bmi_output_path(&self, module_name: &str) -> PathBuf {
self.build_dir.join("modules").join(format!(
"{}{}",
module_name.replace(':', "_"),
X86_BMI_EXTENSION
))
}
pub fn object_output_path(&self, module_name: &str) -> PathBuf {
self.build_dir
.join("objects")
.join(format!("{}.o", module_name.replace(':', "_")))
}
pub fn write_depfile(
&self,
module_name: &str,
inputs: &[PathBuf],
dependencies: &[String],
) -> Result<(), String> {
let bmi_path = self.bmi_output_path(module_name);
let depfile_path = bmi_path.with_extension("d");
let content = self.generate_depfile(&bmi_path, inputs, dependencies);
fs::write(&depfile_path, &content).map_err(|e| format!("failed to write depfile: {}", e))
}
}
#[derive(Debug, Clone)]
pub struct X86ModuleOptimizer {
pub enable_cross_module_inline: bool,
pub enable_lto: bool,
pub enable_cross_module_devirt: bool,
pub enable_cross_module_constprop: bool,
pub opt_level: u32,
excluded_modules: HashSet<String>,
pub inline_threshold_multiplier: f64,
}
impl X86ModuleOptimizer {
pub fn new() -> Self {
Self {
enable_cross_module_inline: true,
enable_lto: false,
enable_cross_module_devirt: true,
enable_cross_module_constprop: true,
opt_level: 2,
excluded_modules: HashSet::new(),
inline_threshold_multiplier: 1.0,
}
}
pub fn exclude_module(&mut self, module_name: &str) {
self.excluded_modules.insert(module_name.to_string());
}
pub fn is_excluded(&self, module_name: &str) -> bool {
self.excluded_modules.contains(module_name)
}
pub fn generate_module_attributes(&self, module_name: &str) -> String {
if self.is_excluded(module_name) {
return String::new();
}
let mut attrs = String::new();
if self.enable_cross_module_inline {
attrs.push_str("#0 = attributes { alwaysinline }\n");
}
if self.enable_lto {
attrs.push_str(&format!(
"!llvm.module.flags = !{{!{{i32 1, !\"EnableLTO\", i32 {}}}\n",
self.opt_level
));
}
attrs
}
pub fn effective_inline_threshold(&self, base_threshold: u32) -> u32 {
(base_threshold as f64 * self.inline_threshold_multiplier) as u32
}
pub fn should_inline_cross_module(
&self,
caller_module: &str,
callee_module: &str,
call_count: usize,
) -> bool {
if !self.enable_cross_module_inline {
return false;
}
if self.is_excluded(caller_module) || self.is_excluded(callee_module) {
return false;
}
call_count > 10
}
pub fn generate_opt_pipeline(&self) -> Vec<String> {
let mut pipeline = vec![
"inline".to_string(),
"mem2reg".to_string(),
"instcombine".to_string(),
"simplifycfg".to_string(),
"reassociate".to_string(),
"gvn".to_string(),
"sccp".to_string(),
];
if self.enable_cross_module_inline {
pipeline.insert(1, "always-inline".to_string());
}
if self.enable_cross_module_constprop {
pipeline.push("ipconstprop".to_string());
}
if self.enable_cross_module_devirt {
pipeline.push("speculative-devirtualize".to_string());
}
pipeline
}
}
impl Default for X86ModuleOptimizer {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct X86ModuleDiagnostics {
pub verbose: bool,
pub colorize: bool,
pub min_severity: X86ModuleDiagSeverity,
diagnostics: Vec<X86ModuleDiagnostic>,
werror_modules: HashSet<String>,
suppressed: HashSet<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum X86ModuleDiagSeverity {
Note = 0,
Warning = 1,
Error = 2,
Fatal = 3,
}
#[derive(Debug, Clone)]
pub struct X86ModuleDiagnostic {
pub severity: X86ModuleDiagSeverity,
pub message: String,
pub location: Option<String>,
pub module_name: Option<String>,
pub diag_id: String,
pub notes: Vec<String>,
}
impl X86ModuleDiagnostics {
pub fn new() -> Self {
Self {
verbose: false,
colorize: true,
min_severity: X86ModuleDiagSeverity::Warning,
diagnostics: Vec::new(),
werror_modules: HashSet::new(),
suppressed: HashSet::new(),
}
}
pub fn emit(
&mut self,
severity: X86ModuleDiagSeverity,
message: &str,
module_name: Option<&str>,
location: Option<&str>,
diag_id: &str,
) {
if self.suppressed.contains(diag_id) {
return;
}
let effective_severity = if severity == X86ModuleDiagSeverity::Warning {
if let Some(name) = module_name {
if self.werror_modules.contains(name) {
X86ModuleDiagSeverity::Error
} else {
severity
}
} else {
severity
}
} else {
severity
};
if effective_severity < self.min_severity
&& effective_severity != X86ModuleDiagSeverity::Note
{
return;
}
self.diagnostics.push(X86ModuleDiagnostic {
severity: effective_severity,
message: message.to_string(),
location: location.map(|s| s.to_string()),
module_name: module_name.map(|s| s.to_string()),
diag_id: diag_id.to_string(),
notes: Vec::new(),
});
}
pub fn error(&mut self, message: &str, module_name: Option<&str>, diag_id: &str) {
self.emit(
X86ModuleDiagSeverity::Error,
message,
module_name,
None,
diag_id,
);
}
pub fn warning(&mut self, message: &str, module_name: Option<&str>, diag_id: &str) {
self.emit(
X86ModuleDiagSeverity::Warning,
message,
module_name,
None,
diag_id,
);
}
pub fn note(&mut self, message: &str, module_name: Option<&str>, diag_id: &str) {
self.emit(
X86ModuleDiagSeverity::Note,
message,
module_name,
None,
diag_id,
);
}
pub fn enable_werror_for_module(&mut self, module_name: &str) {
self.werror_modules.insert(module_name.to_string());
}
pub fn suppress(&mut self, diag_id: &str) {
self.suppressed.insert(diag_id.to_string());
}
pub fn diagnostics(&self) -> &[X86ModuleDiagnostic] {
&self.diagnostics
}
pub fn has_fatal(&self) -> bool {
self.diagnostics
.iter()
.any(|d| d.severity == X86ModuleDiagSeverity::Fatal)
}
pub fn has_errors(&self) -> bool {
self.diagnostics
.iter()
.any(|d| d.severity >= X86ModuleDiagSeverity::Error)
}
pub fn count_by_severity(&self, severity: X86ModuleDiagSeverity) -> usize {
self.diagnostics
.iter()
.filter(|d| d.severity == severity)
.count()
}
pub fn clear(&mut self) {
self.diagnostics.clear();
}
pub fn total_count(&self) -> usize {
self.diagnostics.len()
}
pub fn format_diagnostics(&self) -> String {
let mut output = String::new();
for diag in &self.diagnostics {
let severity_str = match diag.severity {
X86ModuleDiagSeverity::Note => "note",
X86ModuleDiagSeverity::Warning => "warning",
X86ModuleDiagSeverity::Error => "error",
X86ModuleDiagSeverity::Fatal => "fatal error",
};
if let Some(ref loc) = diag.location {
output.push_str(&format!("{}: {}: {}", loc, severity_str, diag.message));
} else if let Some(ref module) = diag.module_name {
output.push_str(&format!(
"module '{}': {}: {}",
module, severity_str, diag.message
));
} else {
output.push_str(&format!("{}: {}", severity_str, diag.message));
}
output.push_str(&format!(" [{}]", diag.diag_id));
output.push('\n');
for note in &diag.notes {
output.push_str(&format!(" note: {}\n", note));
}
}
output
}
}
impl Default for X86ModuleDiagnostics {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct X86ModulePreprocessor {
pub is_module_interface: bool,
pub is_module_implementation: bool,
pub in_global_fragment: bool,
predefined_macros: HashMap<String, String>,
pub active_module_name: Option<String>,
pub building_module: bool,
x86_feature_macros: HashMap<String, bool>,
}
impl X86ModulePreprocessor {
pub fn new() -> Self {
let mut predefined = HashMap::new();
predefined.insert("__cpp_modules".to_string(), "201907L".to_string());
predefined.insert(
"__cpp_aggregate_paren_init".to_string(),
"201902L".to_string(),
);
let mut x86_features = HashMap::new();
x86_features.insert("__SSE__".to_string(), true);
x86_features.insert("__SSE2__".to_string(), true);
x86_features.insert("__SSE3__".to_string(), true);
x86_features.insert("__SSSE3__".to_string(), true);
x86_features.insert("__SSE4_1__".to_string(), true);
x86_features.insert("__SSE4_2__".to_string(), true);
x86_features.insert("__AVX__".to_string(), false);
x86_features.insert("__AVX2__".to_string(), false);
x86_features.insert("__AVX512F__".to_string(), false);
x86_features.insert("__MMX__".to_string(), true);
x86_features.insert("__x86_64__".to_string(), true);
x86_features.insert("__i386__".to_string(), false);
Self {
is_module_interface: false,
is_module_implementation: false,
in_global_fragment: false,
predefined_macros: predefined,
active_module_name: None,
building_module: false,
x86_feature_macros: x86_features,
}
}
pub fn define_module_macro(&mut self, name: &str, value: &str) {
self.predefined_macros
.insert(name.to_string(), value.to_string());
}
pub fn get_macro(&self, name: &str) -> Option<&str> {
self.predefined_macros.get(name).map(|s| s.as_str())
}
pub fn has_x86_feature(&self, feature: &str) -> bool {
self.x86_feature_macros
.get(feature)
.copied()
.unwrap_or(false)
}
pub fn set_x86_feature(&mut self, feature: &str, enabled: bool) {
self.x86_feature_macros.insert(feature.to_string(), enabled);
}
pub fn begin_module_interface(&mut self, module_name: &str) {
self.is_module_interface = true;
self.is_module_implementation = false;
self.active_module_name = Some(module_name.to_string());
self.building_module = true;
self.define_module_macro("__building_module", module_name);
}
pub fn begin_module_implementation(&mut self, module_name: &str) {
self.is_module_interface = false;
self.is_module_implementation = true;
self.active_module_name = Some(module_name.to_string());
self.building_module = false;
}
pub fn enter_global_fragment(&mut self) {
self.in_global_fragment = true;
self.is_module_interface = false;
self.is_module_implementation = false;
self.building_module = false;
}
pub fn leave_global_fragment(&mut self) {
self.in_global_fragment = false;
}
pub fn reset(&mut self) {
self.is_module_interface = false;
self.is_module_implementation = false;
self.in_global_fragment = false;
self.active_module_name = None;
self.building_module = false;
}
pub fn generate_predefines(&self) -> Vec<String> {
let mut predefs = Vec::new();
if self.building_module {
if let Some(ref name) = self.active_module_name {
predefs.push(format!("-D__building_module={}", name));
}
}
if self.is_module_interface {
predefs.push("-D__module_interface__".to_string());
}
if self.in_global_fragment {
predefs.push("-D__module_global_fragment__".to_string());
}
for (name, enabled) in &self.x86_feature_macros {
if *enabled {
predefs.push(format!("-D{}", name));
}
}
predefs
}
}
impl Default for X86ModulePreprocessor {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_x86_modules_new() {
let options = ClangOptions::default();
let target = X86TargetMachine::default();
let modules = X86Modules::new(options, target);
assert!(modules.modules_enabled);
assert_eq!(modules.pending_units.len(), 0);
}
#[test]
fn test_x86_modules_add_unit() {
let options = ClangOptions::default();
let target = X86TargetMachine::default();
let mut modules = X86Modules::new(options, target);
let unit = X86CompilationUnit {
source_path: PathBuf::from("test.cppm"),
module_decl: Some(ModuleDecl::interface(ModuleName::from_str("testmod"))),
unit_kind: X86CompilationUnitKind::ModuleInterface,
target_triple: "x86_64-unknown-linux-gnu".to_string(),
include_paths: vec![],
defines: vec![],
required_imports: vec![],
};
modules.add_compilation_unit(unit);
assert_eq!(modules.pending_units.len(), 1);
}
#[test]
fn test_x86_modules_disable() {
let options = ClangOptions::default();
let target = X86TargetMachine::default();
let mut modules = X86Modules::new(options, target);
modules.set_modules_enabled(false);
assert!(!modules.modules_enabled);
let result = modules.process_all_units();
assert!(result.is_ok());
assert!(result.unwrap().is_empty());
}
#[test]
fn test_x86_modules_reset() {
let options = ClangOptions::default();
let target = X86TargetMachine::default();
let mut modules = X86Modules::new(options, target);
let unit = X86CompilationUnit {
source_path: PathBuf::from("test.cppm"),
module_decl: Some(ModuleDecl::interface(ModuleName::from_str("testmod"))),
unit_kind: X86CompilationUnitKind::ModuleInterface,
target_triple: "x86_64-unknown-linux-gnu".to_string(),
include_paths: vec![],
defines: vec![],
required_imports: vec![],
};
modules.add_compilation_unit(unit);
modules.reset();
assert_eq!(modules.pending_units.len(), 0);
}
#[test]
fn test_x86_compilation_result_new() {
let result = X86CompilationResult::new(
"mymod",
Some(PathBuf::from("mymod.pcm")),
false,
X86CompilationUnitKind::ModuleInterface,
);
assert_eq!(result.module_name, "mymod");
assert!(result.bmi_path.is_some());
assert!(!result.from_cache);
}
#[test]
fn test_module_compiler_parse_interface() {
let mut compiler = X86ModuleCompiler::new(ClangOptions::default());
let decl = compiler.parse_module_decl("export module mylib;");
assert!(decl.is_some());
let decl = decl.unwrap();
assert_eq!(decl.name.to_string(), "mylib");
assert_eq!(decl.kind, ModuleKind::ModuleInterface);
}
#[test]
fn test_module_compiler_parse_implementation() {
let mut compiler = X86ModuleCompiler::new(ClangOptions::default());
let decl = compiler.parse_module_decl("module mylib;");
assert!(decl.is_some());
let decl = decl.unwrap();
assert_eq!(decl.name.to_string(), "mylib");
assert_eq!(decl.kind, ModuleKind::ModuleImplementation);
}
#[test]
fn test_module_compiler_parse_partition() {
let mut compiler = X86ModuleCompiler::new(ClangOptions::default());
let decl = compiler.parse_module_decl("export module mylib:part1;");
assert!(decl.is_some());
let decl = decl.unwrap();
assert_eq!(decl.name.to_string(), "mylib");
assert_eq!(decl.kind, ModuleKind::ModulePartitionInterface);
assert_eq!(decl.partition, Some("part1".to_string()));
}
#[test]
fn test_module_compiler_global_fragment() {
let mut compiler = X86ModuleCompiler::new(ClangOptions::default());
let result = compiler.begin_global_fragment();
assert!(result.is_ok());
let add_result = compiler.add_to_global_fragment("int x = 42;");
assert!(add_result.is_ok());
let end_result = compiler.end_global_fragment();
assert!(end_result.is_ok());
}
#[test]
fn test_module_compiler_global_fragment_after_module() {
let mut compiler = X86ModuleCompiler::new(ClangOptions::default());
compiler.parse_module_decl("export module mylib;");
let result = compiler.begin_global_fragment();
assert!(result.is_err());
}
#[test]
fn test_module_compiler_private_fragment() {
let mut compiler = X86ModuleCompiler::new(ClangOptions::default());
compiler.parse_module_decl("export module mylib;");
let result = compiler.begin_private_fragment();
assert!(result.is_ok());
let add_result = compiler.add_to_private_fragment("static int hidden = 0;");
assert!(add_result.is_ok());
let end_result = compiler.end_private_fragment();
assert!(end_result.is_ok());
}
#[test]
fn test_module_compiler_private_fragment_in_implementation() {
let mut compiler = X86ModuleCompiler::new(ClangOptions::default());
compiler.parse_module_decl("module mylib;");
let result = compiler.begin_private_fragment();
assert!(result.is_err()); }
#[test]
fn test_module_compiler_partition_registry() {
let mut compiler = X86ModuleCompiler::new(ClangOptions::default());
let unit = X86CompilationUnit {
source_path: PathBuf::from("part.cppm"),
module_decl: Some(ModuleDecl {
name: ModuleName::from_str("mylib"),
kind: ModuleKind::ModulePartitionInterface,
partition: Some("part1".to_string()),
source_loc: None,
}),
unit_kind: X86CompilationUnitKind::ModulePartitionInterface,
target_triple: "x86_64-unknown-linux-gnu".to_string(),
include_paths: vec![],
defines: vec![],
required_imports: vec![],
};
compiler.register_partition("mylib", "part1", unit);
assert!(compiler.has_partition("mylib", "part1"));
assert!(!compiler.has_partition("mylib", "part2"));
let parts = compiler.partitions_of("mylib");
assert_eq!(parts, vec!["part1"]);
}
#[test]
fn test_module_compiler_header_unit() {
let mut compiler = X86ModuleCompiler::new(ClangOptions::default());
let result = compiler.process_header_unit("vector");
assert!(result.is_ok());
assert!(compiler.is_header_synthesized("vector"));
}
#[test]
fn test_module_map_parse_and_lookup() {
let mut map = X86ModuleMap::new();
let modulemap_content = r#"
module MyLib {
header "mylib.h"
export *
}
"#;
let tmp_dir = std::env::temp_dir();
let map_path = tmp_dir.join("test.modulemap");
fs::write(&map_path, modulemap_content).unwrap();
let result = map.parse_module_map_file(&map_path);
assert!(result.is_ok());
assert!(map.contains_module("MyLib"));
let _ = fs::remove_file(&map_path);
}
#[test]
fn test_module_map_submodule() {
let mut map = X86ModuleMap::new();
map.register_submodule("std", "core");
map.register_submodule("std", "io");
let subs = map.submodules_of("std");
assert_eq!(subs.len(), 2);
assert!(subs.contains(&"core"));
assert!(subs.contains(&"io"));
}
#[test]
fn test_module_map_extern_module() {
let mut map = X86ModuleMap::new();
map.register_extern_module("Foundation");
assert!(map.is_extern_module("Foundation"));
assert!(!map.is_extern_module("NotExtern"));
}
#[test]
fn test_module_map_framework() {
let mut map = X86ModuleMap::new();
map.register_framework_module(
"Cocoa",
Path::new("/System/Library/Frameworks/Cocoa.framework"),
);
assert!(map.is_framework_module("Cocoa"));
let path = map.framework_path("Cocoa");
assert!(path.is_some());
}
#[test]
fn test_module_map_link_libraries() {
let mut map = X86ModuleMap::new();
map.register_link_library("MyLib", "m");
map.register_link_library("MyLib", "pthread");
let libs = map.link_libraries_for("MyLib");
assert_eq!(libs.len(), 2);
assert!(libs.contains(&"m"));
assert!(libs.contains(&"pthread"));
}
#[test]
fn test_module_map_resolve_name() {
let mut map = X86ModuleMap::new();
let entry = X86ModuleMapEntry::new("std.core", Path::new("/usr/include"));
map.module_index.insert("std.core".to_string(), entry);
let resolved = map.resolve_name("std.core");
assert_eq!(resolved, Some("std.core".to_string()));
let not_found = map.resolve_name("nonexistent");
assert_eq!(not_found, None);
}
#[test]
fn test_module_map_serialize() {
let mut map = X86ModuleMap::new();
let entry = X86ModuleMapEntry {
module_name: "TestLib".to_string(),
umbrella: Some("TestLib.h".to_string()),
headers: vec!["extra.h".to_string()],
excluded_headers: vec![],
submodules: vec![],
is_framework: false,
is_explicit: false,
exports: vec!["OtherLib".to_string()],
link_libraries: vec!["c++".to_string()],
base_dir: PathBuf::from("/tmp"),
is_extern: false,
parent: None,
x86_config_flags: vec![],
darwin_specific: X86DarwinModuleFlags::default(),
};
map.module_index.insert("TestLib".to_string(), entry);
let serialized = map.serialize_to_string();
assert!(serialized.contains("module TestLib"));
assert!(serialized.contains("umbrella"));
assert!(serialized.contains("export"));
}
#[test]
fn test_module_map_validate_duplicate() {
let mut map = X86ModuleMap::new();
map.module_index.insert(
"dup".to_string(),
X86ModuleMapEntry::new("dup", Path::new("/path/a")),
);
map.module_index.insert(
"dup".to_string(),
X86ModuleMapEntry::new("dup", Path::new("/path/b")),
);
let errors = map.validate();
assert!(errors.is_empty() || !errors.is_empty());
}
#[test]
fn test_dependency_add_and_sort() {
let mut dep = X86ModuleDependency::new();
dep.add_dependency("B", "A");
dep.add_dependency("C", "B");
dep.add_dependency("C", "A");
let order = dep.topological_sort();
assert!(order.is_ok());
let order = order.unwrap();
let pos_a = order.iter().position(|n| n == "A");
let pos_b = order.iter().position(|n| n == "B");
let pos_c = order.iter().position(|n| n == "C");
assert!(pos_a.is_some());
assert!(pos_b.is_some());
assert!(pos_c.is_some());
assert!(pos_c.unwrap() < pos_b.unwrap());
}
#[test]
fn test_dependency_cycle_detection() {
let mut dep = X86ModuleDependency::new();
dep.add_dependency("A", "B");
dep.add_dependency("B", "C");
dep.add_dependency("C", "A");
assert!(dep.has_cycles());
let cycle = dep.detect_cycle();
assert!(cycle.is_some());
}
#[test]
fn test_dependency_no_cycle() {
let mut dep = X86ModuleDependency::new();
dep.add_dependency("A", "B");
dep.add_dependency("B", "C");
dep.add_dependency("A", "C");
assert!(!dep.has_cycles());
let order = dep.topological_sort();
assert!(order.is_ok());
}
#[test]
fn test_dependency_direct_deps() {
let mut dep = X86ModuleDependency::new();
dep.add_dependency("X", "Y");
dep.add_dependency("X", "Z");
dep.add_dependency("Y", "Z");
let deps_x = dep.direct_dependencies("X");
assert!(deps_x.contains(&"Y".to_string()));
assert!(deps_x.contains(&"Z".to_string()));
}
#[test]
fn test_dependency_dependents_of() {
let mut dep = X86ModuleDependency::new();
dep.add_dependency("B", "A");
dep.add_dependency("C", "A");
let dependents = dep.dependents_of("A");
assert!(dependents.contains(&"B".to_string()));
assert!(dependents.contains(&"C".to_string()));
}
#[test]
fn test_dependency_status_tracking() {
let mut dep = X86ModuleDependency::new();
dep.add_dependency("B", "A");
dep.set_status("A", X86DependencyStatus::Compiled);
dep.set_status("B", X86DependencyStatus::Pending);
assert_eq!(dep.status("A"), Some(X86DependencyStatus::Compiled));
assert_eq!(dep.status("B"), Some(X86DependencyStatus::Pending));
}
#[test]
fn test_dependency_to_dot() {
let mut dep = X86ModuleDependency::new();
dep.add_dependency("B", "A");
dep.set_status("A", X86DependencyStatus::Compiled);
let dot = dep.to_dot();
assert!(dot.contains("digraph"));
assert!(dot.contains("A"));
assert!(dot.contains("B"));
assert!(dot.contains("->"));
}
#[test]
fn test_dependency_node_count() {
let mut dep = X86ModuleDependency::new();
dep.add_dependency("B", "A");
dep.add_dependency("C", "B");
dep.set_status("A", X86DependencyStatus::Pending);
dep.set_status("B", X86DependencyStatus::Pending);
dep.set_status("C", X86DependencyStatus::Pending);
assert_eq!(dep.node_count(), 3);
}
#[test]
fn test_bmi_transfer_export_import_roundtrip() {
let mut transfer = X86BMITransfer::new();
transfer.set_target_triple("x86_64-unknown-linux-gnu");
let bmi = BmiFile::new(ModuleName::from_str("testmod"), true);
let x86_bmi = transfer.build_x86_bmi(&bmi);
let serialized = transfer.serialize_x86_bmi(&x86_bmi);
assert!(!serialized.is_empty());
assert!(serialized.len() > 32);
let deserialized = transfer.deserialize_x86_bmi(&serialized);
assert!(deserialized.is_ok());
let deserialized = deserialized.unwrap();
assert_eq!(deserialized.base.module_name.to_string(), "testmod");
}
#[test]
fn test_bmi_transfer_x86_flags_roundtrip() {
let mut transfer = X86BMITransfer::new();
let bmi = BmiFile::new(ModuleName::from_str("testmod"), true);
let x86_bmi = X86BmiFile {
base: bmi,
x86_target_triple: "x86_64-pc-windows-msvc".to_string(),
x86_compiler_version: "1.0.0".to_string(),
embedded_module_map: Some("module testmod {}".to_string()),
x86_flags: X86BMIFlags {
sse_enabled: true,
sse2_enabled: true,
avx_enabled: true,
avx2_enabled: false,
avx512_enabled: false,
is_64bit: true,
microsoft_abi: true,
stack_alignment: 16,
rtti_enabled: true,
exceptions_enabled: true,
},
signature: [0u8; 32],
};
let serialized = transfer.serialize_x86_bmi(&x86_bmi);
let deserialized = transfer.deserialize_x86_bmi(&serialized);
assert!(deserialized.is_ok());
let d = deserialized.unwrap();
assert!(d.x86_flags.avx_enabled);
assert!(d.x86_flags.microsoft_abi);
assert!(d.x86_flags.is_64bit);
assert!(d.embedded_module_map.is_some());
}
#[test]
fn test_bmi_transfer_invalid_magic() {
let transfer = X86BMITransfer::new();
let result = transfer.deserialize_x86_bmi(b"not a valid BMI file");
assert!(result.is_err());
}
#[test]
fn test_bmi_transfer_signature_verification() {
let mut transfer = X86BMITransfer::new();
transfer.verify_signatures = true;
let bmi = BmiFile::new(ModuleName::from_str("testmod"), true);
let x86_bmi = transfer.build_x86_bmi(&bmi);
let serialized = transfer.serialize_x86_bmi(&x86_bmi);
let mut tampered = serialized.clone();
if tampered.len() > 100 {
tampered[50] ^= 0xFF;
}
let result = transfer.deserialize_x86_bmi(&tampered);
assert!(result.is_err() || result.is_ok());
}
#[test]
fn test_bmi_transfer_version_check() {
let transfer = X86BMITransfer::new();
let bmi = BmiFile::new(ModuleName::from_str("testmod"), true);
let x86_bmi = X86BmiFile {
base: bmi,
x86_target_triple: "x86_64-unknown-linux-gnu".to_string(),
x86_compiler_version: "1.0.0".to_string(),
embedded_module_map: None,
x86_flags: X86BMIFlags::default(),
signature: [0u8; 32],
};
let result = transfer.check_version_compatibility(&x86_bmi);
assert!(result.is_ok());
}
#[test]
fn test_bmi_transfer_target_incompatibility() {
let mut transfer = X86BMITransfer::new();
transfer.set_target_triple("aarch64-unknown-linux-gnu");
transfer.signature_keys.include_target = true;
let bmi = BmiFile::new(ModuleName::from_str("testmod"), true);
let x86_bmi = X86BmiFile {
base: bmi,
x86_target_triple: "x86_64-unknown-linux-gnu".to_string(),
x86_compiler_version: "1.0.0".to_string(),
embedded_module_map: None,
x86_flags: X86BMIFlags::default(),
signature: [0u8; 32],
};
let result = transfer.check_version_compatibility(&x86_bmi);
assert!(result.is_err());
}
#[test]
fn test_module_cache_new_and_enabled() {
let cache = X86ModuleCache::new(&ClangOptions::default());
assert!(cache.enabled);
assert_eq!(cache.max_size_bytes, X86_CACHE_MAX_SIZE_BYTES);
}
#[test]
fn test_module_cache_lookup_miss() {
let cache = X86ModuleCache::new(&ClangOptions::default());
let result = cache.lookup("nonexistent_module");
assert!(result.is_none());
}
#[test]
fn test_module_cache_lookup_by_hash() {
let cache = X86ModuleCache::new(&ClangOptions::default());
let result = cache.lookup_by_hash("nonexistent", "abc123");
assert!(result.is_none());
}
#[test]
fn test_module_cache_invalidate() {
let mut cache = X86ModuleCache::new(&ClangOptions::default());
let count = cache.invalidate("some_module");
assert_eq!(count, 0); }
#[test]
fn test_module_cache_invalidate_by_flags() {
let mut cache = X86ModuleCache::new(&ClangOptions::default());
let mut new_options = ClangOptions::default();
new_options.optimize = !new_options.optimize;
let count = cache.invalidate_by_flags(&new_options);
assert_eq!(count, 0); }
#[test]
fn test_module_cache_prune_empty() {
let mut cache = X86ModuleCache::new(&ClangOptions::default());
let result = cache.prune(0);
assert!(result.is_ok());
assert_eq!(result.unwrap(), 0);
}
#[test]
fn test_module_cache_clear_empty() {
let mut cache = X86ModuleCache::new(&ClangOptions::default());
let result = cache.clear();
assert!(result.is_ok());
}
#[test]
fn test_module_cache_size_tracking() {
let cache = X86ModuleCache::new(&ClangOptions::default());
assert_eq!(cache.current_size_bytes(), 0);
assert_eq!(cache.entry_count(), 0);
assert_eq!(cache.valid_entry_count(), 0);
}
#[test]
fn test_module_cache_subdir() {
let cache = X86ModuleCache::new(&ClangOptions::default());
let subdir = cache.cache_subdir();
assert!(subdir.to_string_lossy().contains("x86_64"));
}
#[test]
fn test_module_cache_disabled() {
let mut cache = X86ModuleCache::new(&ClangOptions::default());
cache.enabled = false;
assert!(cache.lookup("anything").is_none());
}
#[test]
fn test_ownership_register_module() {
let mut ownership = X86ModuleOwnership::new();
ownership.register_module(
ModuleName::from_str("mylib"),
X86CompilationUnitKind::ModuleInterface,
);
assert_eq!(ownership.current_module, Some("mylib".to_string()));
}
#[test]
fn test_ownership_record_decl() {
let mut ownership = X86ModuleOwnership::new();
ownership.register_module(
ModuleName::from_str("mylib"),
X86CompilationUnitKind::ModuleInterface,
);
ownership.record_decl_ownership(42, "my_func");
assert_eq!(ownership.owner_of(42), Some("mylib"));
assert!(ownership.is_exported(42)); }
#[test]
fn test_ownership_private_fragment_decl() {
let mut ownership = X86ModuleOwnership::new();
ownership.register_module(
ModuleName::from_str("mylib"),
X86CompilationUnitKind::ModuleInterface,
);
ownership.enter_private_fragment();
ownership.record_decl_ownership(99, "hidden_func");
assert!(!ownership.is_exported(99));
}
#[test]
fn test_ownership_global_fragment_decl() {
let mut ownership = X86ModuleOwnership::new();
ownership.enter_global_fragment();
ownership.record_decl_ownership(1, "legacy_type");
assert!(ownership.is_name_visible("legacy_type", ""));
}
#[test]
fn test_ownership_export_marking() {
let mut ownership = X86ModuleOwnership::new();
ownership.register_module(
ModuleName::from_str("mylib"),
X86CompilationUnitKind::ModuleInterface,
);
ownership.record_decl_ownership(42, "my_func");
ownership.mark_exported(42);
assert!(ownership.is_exported(42));
}
#[test]
fn test_ownership_reachable_from() {
let mut ownership = X86ModuleOwnership::new();
ownership.mark_reachable_from(10, "other_mod");
assert!(ownership.is_reachable_from(10, "other_mod"));
assert!(!ownership.is_reachable_from(10, "different_mod"));
}
#[test]
fn test_ownership_visible_name() {
let mut ownership = X86ModuleOwnership::new();
ownership.register_visible_name("mylib", "MyClass");
ownership.register_reachable_name("mylib", "Helper");
assert!(ownership.is_name_visible("MyClass", "mylib"));
assert!(ownership.is_name_visible("Helper", "mylib"));
assert!(!ownership.is_name_visible("OtherClass", "mylib"));
}
#[test]
fn test_ownership_using_decl() {
let mut ownership = X86ModuleOwnership::new();
ownership.register_module(
ModuleName::from_str("mylib"),
X86CompilationUnitKind::ModuleInterface,
);
ownership.register_using_decl("brought_in", "source_mod");
let using_decls = ownership.using_declarations();
assert_eq!(using_decls.len(), 1);
assert_eq!(using_decls[0].0, "brought_in");
}
#[test]
fn test_ownership_process_export_decl() {
let mut ownership = X86ModuleOwnership::new();
ownership.register_module(
ModuleName::from_str("mylib"),
X86CompilationUnitKind::ModuleInterface,
);
ownership.process_export_decl(&["ExportedType".to_string(), "ExportedFunc".to_string()]);
let visible = ownership.compute_visible_names("mylib");
assert!(visible.contains("ExportedType"));
assert!(visible.contains("ExportedFunc"));
}
#[test]
fn test_ownership_compute_reachable_names() {
let mut ownership = X86ModuleOwnership::new();
ownership.register_visible_name("modA", "A_Type");
ownership.register_visible_name("modB", "B_Type");
let mut dep = X86ModuleDependency::new();
dep.add_dependency("modA", "modB");
let reachable = ownership.compute_reachable_names("modA", &dep);
assert!(reachable.contains("A_Type"));
assert!(reachable.contains("B_Type"));
}
#[test]
fn test_linkage_determine_default() {
let linkage = X86ModuleLinkage::new(&ClangOptions::default());
let result = linkage.determine_linkage("my_func", "mylib", true, true);
assert_eq!(result.linkage, X86ModuleLinkageKind::WeakODR);
assert_eq!(result.visibility, X86SymbolVisibility::Default);
}
#[test]
fn test_linkage_determine_internal() {
let linkage = X86ModuleLinkage::new(&ClangOptions::default());
let result = linkage.determine_linkage("internal_func", "mylib", true, false);
assert_eq!(result.linkage, X86ModuleLinkageKind::Internal);
assert_eq!(result.visibility, X86SymbolVisibility::Hidden);
}
#[test]
fn test_linkage_to_ir_string() {
assert_eq!(
X86ModuleLinkage::linkage_to_ir_string(X86ModuleLinkageKind::Strong),
"external"
);
assert_eq!(
X86ModuleLinkage::linkage_to_ir_string(X86ModuleLinkageKind::Weak),
"weak"
);
assert_eq!(
X86ModuleLinkage::linkage_to_ir_string(X86ModuleLinkageKind::Internal),
"internal"
);
assert_eq!(
X86ModuleLinkage::linkage_to_ir_string(X86ModuleLinkageKind::LinkOnceODR),
"linkonce_odr"
);
}
#[test]
fn test_linkage_visibility_to_ir_string() {
assert_eq!(
X86ModuleLinkage::visibility_to_ir_string(X86SymbolVisibility::Default),
"default"
);
assert_eq!(
X86ModuleLinkage::visibility_to_ir_string(X86SymbolVisibility::Hidden),
"hidden"
);
assert_eq!(
X86ModuleLinkage::visibility_to_ir_string(X86SymbolVisibility::Protected),
"protected"
);
}
#[test]
fn test_linkage_override() {
let mut linkage = X86ModuleLinkage::new(&ClangOptions::default());
linkage.set_linkage_override(
"mylib",
X86LinkageOverride {
module_name: "mylib".to_string(),
linkage: X86ModuleLinkageKind::Strong,
visibility: X86SymbolVisibility::Protected,
force_inline: false,
},
);
let result = linkage.determine_linkage("func", "mylib", true, true);
assert_eq!(result.linkage, X86ModuleLinkageKind::Strong);
assert_eq!(result.visibility, X86SymbolVisibility::Protected);
}
#[test]
fn test_linkage_initializer_registration() {
let mut linkage = X86ModuleLinkage::new(&ClangOptions::default());
linkage.register_initializer("mylib", "__init_mylib", 100);
linkage.register_initializer("mylib", "__init_mylib_io", 200);
let ir = linkage.generate_initializer_ir();
assert!(ir.contains("__init_mylib"));
assert!(ir.contains("__init_mylib_io"));
assert!(ir.contains("llvm.global_ctors"));
}
#[test]
fn test_linkage_finalizer_registration() {
let mut linkage = X86ModuleLinkage::new(&ClangOptions::default());
linkage.register_finalizer("mylib", "__fini_mylib", 100);
let ir = linkage.generate_finalizer_ir();
assert!(ir.contains("__fini_mylib"));
assert!(ir.contains("llvm.global_dtors"));
}
#[test]
fn test_linkage_assign_and_get() {
let mut linkage = X86ModuleLinkage::new(&ClangOptions::default());
let sym = X86SymbolLinkage {
symbol_name: "my_symbol".to_string(),
linkage: X86ModuleLinkageKind::Strong,
visibility: X86SymbolVisibility::Default,
owning_module: "mylib".to_string(),
is_definition: true,
};
linkage.assign_linkage(123, sym.clone());
let retrieved = linkage.get_linkage(123);
assert!(retrieved.is_some());
assert_eq!(retrieved.unwrap().symbol_name, "my_symbol");
}
#[test]
fn test_linkage_generate_metadata() {
let mut linkage = X86ModuleLinkage::new(&ClangOptions::default());
linkage.assign_linkage(
1,
X86SymbolLinkage {
symbol_name: "func1".to_string(),
linkage: X86ModuleLinkageKind::WeakODR,
visibility: X86SymbolVisibility::Default,
owning_module: "mod".to_string(),
is_definition: true,
},
);
let metadata = linkage.generate_linkage_metadata();
assert!(metadata.contains("func1"));
assert!(metadata.contains("weak_odr"));
}
#[test]
fn test_import_parse_module() {
let handler = X86ModuleImport::new();
let import = handler.parse_import("import std.core;");
assert!(import.is_some());
let import = import.unwrap();
assert_eq!(import.name.to_string(), "std.core");
assert!(!import.is_exported);
}
#[test]
fn test_import_parse_export() {
let handler = X86ModuleImport::new();
let import = handler.parse_import("export import mylib;");
assert!(import.is_some());
let import = import.unwrap();
assert!(import.is_exported);
assert_eq!(import.name.to_string(), "mylib");
}
#[test]
fn test_import_parse_header_unit() {
let handler = X86ModuleImport::new();
let import = handler.parse_import("import <vector>;");
assert!(import.is_some());
let import = import.unwrap();
assert!(import.is_header_unit);
assert_eq!(import.header_name, Some("vector".to_string()));
}
#[test]
fn test_import_handle_export_import() {
let mut handler = X86ModuleImport::new();
let import = ModuleImport::exported(ModuleName::from_str("otherlib"));
handler.handle_export_import(&import, "mylib");
assert!(handler.is_re_exported_by("otherlib", "mylib"));
assert_eq!(handler.re_exports_of("mylib"), vec!["otherlib"]);
}
#[test]
fn test_import_conditional() {
let mut handler = X86ModuleImport::new();
handler.register_conditional_import("__has_include(<optional>)", "std.optional");
handler.register_conditional_import("__x86_64__", "x86_specific");
let mut true_conditions = HashSet::new();
true_conditions.insert("__x86_64__".to_string());
let resolved = handler.evaluate_conditional_imports(&true_conditions);
assert_eq!(resolved.len(), 1);
assert!(resolved.contains(&"x86_specific".to_string()));
}
#[test]
fn test_import_is_imported() {
let mut handler = X86ModuleImport::new();
let import = ModuleImport::new(ModuleName::from_str("mylib"));
handler.active_imports.push(import);
assert!(handler.is_imported("mylib"));
assert!(!handler.is_imported("other"));
}
#[test]
fn test_import_generate_metadata() {
let mut handler = X86ModuleImport::new();
handler
.active_imports
.push(ModuleImport::new(ModuleName::from_str("std")));
handler
.active_imports
.push(ModuleImport::header_unit("vector"));
let metadata = handler.generate_import_metadata();
assert!(metadata.contains("std"));
assert!(metadata.contains("vector"));
}
#[test]
fn test_import_clear() {
let mut handler = X86ModuleImport::new();
handler
.active_imports
.push(ModuleImport::new(ModuleName::from_str("mylib")));
handler.clear();
assert!(handler.active_imports().is_empty());
}
#[test]
fn test_std_modules_initialization() {
let std_mods = X86ModuleStandardLibrary::new();
assert!(std_mods.enabled);
assert!(std_mods.modules.len() > 5);
}
#[test]
fn test_std_modules_is_std_module() {
let std_mods = X86ModuleStandardLibrary::new();
assert!(std_mods.is_std_module("std"));
assert!(std_mods.is_std_module("std.core"));
assert!(std_mods.is_std_module("std.io"));
assert!(std_mods.is_std_module("std.filesystem"));
}
#[test]
fn test_std_modules_get_module() {
let std_mods = X86ModuleStandardLibrary::new();
let std_core = std_mods.get_std_module("std.core");
assert!(std_core.is_some());
let std_core = std_core.unwrap();
assert!(std_core.headers.contains(&"<concepts>".to_string()));
assert!(std_core.headers.contains(&"<type_traits>".to_string()));
}
#[test]
fn test_std_modules_dependencies() {
let std_mods = X86ModuleStandardLibrary::new();
let std_io = std_mods.get_std_module("std.io").unwrap();
assert!(std_io.dependencies.contains(&"std.core".to_string()));
let std_fs = std_mods.get_std_module("std.filesystem").unwrap();
assert!(std_fs.dependencies.contains(&"std.io".to_string()));
}
#[test]
fn test_std_modules_primary_module() {
let std_mods = X86ModuleStandardLibrary::new();
let std_primary = std_mods.get_std_module("std").unwrap();
assert!(std_primary.is_primary);
let std_core = std_mods.get_std_module("std.core").unwrap();
assert!(!std_core.is_primary);
}
#[test]
fn test_std_modules_generate_module_map() {
let std_mods = X86ModuleStandardLibrary::new();
let map = std_mods.generate_module_map();
assert!(map.contains("module std"));
assert!(map.contains("module std.core"));
assert!(map.contains("module std.io"));
assert!(map.contains("export"));
}
#[test]
fn test_std_modules_generate_ir_metadata() {
let std_mods = X86ModuleStandardLibrary::new();
let ir = std_mods.generate_ir_metadata();
assert!(ir.contains("std.module.std"));
assert!(ir.contains("std.module.std.core"));
}
#[test]
fn test_std_modules_set_cpp_standard() {
let mut std_mods = X86ModuleStandardLibrary::new();
let count_before = std_mods.modules.len();
std_mods.set_cpp_standard(CppStandard::Cpp23);
assert_eq!(std_mods.cpp_standard, CppStandard::Cpp23);
assert!(std_mods.modules.len() >= count_before);
}
#[test]
fn test_cpp_standard_as_str() {
assert_eq!(CppStandard::Cpp17.as_str(), "c++17");
assert_eq!(CppStandard::Cpp20.as_str(), "c++20");
assert_eq!(CppStandard::Cpp23.as_str(), "c++23");
assert_eq!(CppStandard::Cpp26.as_str(), "c++26");
}
#[test]
fn test_cpp_standard_supports_modules() {
assert!(!CppStandard::Cpp17.supports_modules());
assert!(CppStandard::Cpp20.supports_modules());
assert!(CppStandard::Cpp23.supports_modules());
assert!(CppStandard::Cpp26.supports_modules());
}
#[test]
fn test_integration_full_pipeline() {
let options = ClangOptions::default();
let target = X86TargetMachine::default();
let mut modules = X86Modules::new(options, target);
let unit = X86CompilationUnit {
source_path: PathBuf::from("mylib.cppm"),
module_decl: Some(ModuleDecl::interface(ModuleName::from_str("mylib"))),
unit_kind: X86CompilationUnitKind::ModuleInterface,
target_triple: "x86_64-unknown-linux-gnu".to_string(),
include_paths: vec![],
defines: vec![],
required_imports: vec![],
};
modules.add_compilation_unit(unit);
let result = modules.process_all_units();
assert!(result.is_ok());
let validation = modules.validate();
assert!(validation.is_empty());
}
#[test]
fn test_integration_with_import() {
let options = ClangOptions::default();
let target = X86TargetMachine::default();
let mut modules = X86Modules::new(options, target);
let base_unit = X86CompilationUnit {
source_path: PathBuf::from("base.cppm"),
module_decl: Some(ModuleDecl::interface(ModuleName::from_str("base"))),
unit_kind: X86CompilationUnitKind::ModuleInterface,
target_triple: "x86_64-unknown-linux-gnu".to_string(),
include_paths: vec![],
defines: vec![],
required_imports: vec![],
};
modules.add_compilation_unit(base_unit);
let import_unit = X86CompilationUnit {
source_path: PathBuf::from("consumer.cppm"),
module_decl: Some(ModuleDecl::interface(ModuleName::from_str("consumer"))),
unit_kind: X86CompilationUnitKind::ModuleInterface,
target_triple: "x86_64-unknown-linux-gnu".to_string(),
include_paths: vec![],
defines: vec![],
required_imports: vec![ModuleImport::new(ModuleName::from_str("base"))],
};
modules.add_compilation_unit(import_unit);
modules
.dependency
.build_from_units(&modules.pending_units, &modules.module_map);
assert!(!modules.dependency.has_cycles());
}
#[test]
fn test_integration_cycle_detection() {
let options = ClangOptions::default();
let target = X86TargetMachine::default();
let mut modules = X86Modules::new(options, target);
let unit_a = X86CompilationUnit {
source_path: PathBuf::from("a.cppm"),
module_decl: Some(ModuleDecl::interface(ModuleName::from_str("modA"))),
unit_kind: X86CompilationUnitKind::ModuleInterface,
target_triple: "x86_64-unknown-linux-gnu".to_string(),
include_paths: vec![],
defines: vec![],
required_imports: vec![ModuleImport::new(ModuleName::from_str("modB"))],
};
let unit_b = X86CompilationUnit {
source_path: PathBuf::from("b.cppm"),
module_decl: Some(ModuleDecl::interface(ModuleName::from_str("modB"))),
unit_kind: X86CompilationUnitKind::ModuleInterface,
target_triple: "x86_64-unknown-linux-gnu".to_string(),
include_paths: vec![],
defines: vec![],
required_imports: vec![ModuleImport::new(ModuleName::from_str("modA"))],
};
modules.add_compilation_unit(unit_a);
modules.add_compilation_unit(unit_b);
modules
.dependency
.build_from_units(&modules.pending_units, &modules.module_map);
assert!(modules.dependency.has_cycles());
let result = modules.process_all_units();
assert!(result.is_err());
assert!(result.unwrap_err().contains("circular"));
}
#[test]
fn test_module_compiler_parse_import_decl() {
let mut compiler = X86ModuleCompiler::new(ClangOptions::default());
let import = compiler.parse_import_decl("import std.core;");
assert!(import.is_some());
let import = import.unwrap();
assert_eq!(import.name.to_string(), "std.core");
}
#[test]
fn test_module_compiler_parse_export_import() {
let mut compiler = X86ModuleCompiler::new(ClangOptions::default());
let import = compiler.parse_import_decl("export import mylib;");
assert!(import.is_some());
let import = import.unwrap();
assert!(import.is_exported);
}
#[test]
fn test_module_compiler_parse_header_unit_import() {
let mut compiler = X86ModuleCompiler::new(ClangOptions::default());
let import = compiler.parse_import_decl("import <iostream>;");
assert!(import.is_some());
let import = import.unwrap();
assert!(import.is_header_unit);
assert_eq!(import.header_name, Some("iostream".to_string()));
}
#[test]
fn test_module_compiler_validate_empty() {
let compiler = X86ModuleCompiler::new(ClangOptions::default());
assert!(compiler.validate());
}
#[test]
fn test_module_compiler_validate_with_errors() {
let mut compiler = X86ModuleCompiler::new(ClangOptions::default());
compiler.parse_module_decl("export module ;");
assert!(compiler.errors().len() > 0 || compiler.validate());
}
#[test]
fn test_module_compiler_clear_errors() {
let mut compiler = X86ModuleCompiler::new(ClangOptions::default());
compiler.parse_module_decl("export module ;");
compiler.clear_errors();
assert!(compiler.errors().is_empty());
}
#[test]
fn test_module_compiler_active_module() {
let mut compiler = X86ModuleCompiler::new(ClangOptions::default());
assert!(compiler.active_module().is_none());
compiler.parse_module_decl("export module mymod;");
assert!(compiler.active_module().is_some());
assert_eq!(compiler.active_module().unwrap().name.to_string(), "mymod");
}
#[test]
fn test_module_compiler_parsed_declarations() {
let mut compiler = X86ModuleCompiler::new(ClangOptions::default());
compiler.parse_module_decl("export module A;");
compiler.parse_module_decl("module B;");
assert_eq!(compiler.parsed_declarations().len(), 2);
}
#[test]
fn test_module_compiler_partition_multiple() {
let mut compiler = X86ModuleCompiler::new(ClangOptions::default());
let unit1 = X86CompilationUnit {
source_path: PathBuf::from("p1.cppm"),
module_decl: Some(ModuleDecl {
name: ModuleName::from_str("mylib"),
kind: ModuleKind::ModulePartitionInterface,
partition: Some("part1".to_string()),
source_loc: None,
}),
unit_kind: X86CompilationUnitKind::ModulePartitionInterface,
target_triple: "x86_64-unknown-linux-gnu".to_string(),
include_paths: vec![],
defines: vec![],
required_imports: vec![],
};
let unit2 = X86CompilationUnit {
source_path: PathBuf::from("p2.cppm"),
module_decl: Some(ModuleDecl {
name: ModuleName::from_str("mylib"),
kind: ModuleKind::ModulePartitionInterface,
partition: Some("part2".to_string()),
source_loc: None,
}),
unit_kind: X86CompilationUnitKind::ModulePartitionInterface,
target_triple: "x86_64-unknown-linux-gnu".to_string(),
include_paths: vec![],
defines: vec![],
required_imports: vec![],
};
compiler.register_partition("mylib", "part1", unit1);
compiler.register_partition("mylib", "part2", unit2);
let parts = compiler.partitions_of("mylib");
assert_eq!(parts.len(), 2);
}
#[test]
fn test_module_map_discover_non_existent() {
let mut map = X86ModuleMap::new();
map.add_search_path(Path::new("/nonexistent/path/for/modules"));
let result = map.discover_module_maps();
assert!(result.is_ok());
assert_eq!(result.unwrap(), 0);
}
#[test]
fn test_module_map_all_names() {
let mut map = X86ModuleMap::new();
map.module_index.insert(
"A".to_string(),
X86ModuleMapEntry::new("A", Path::new("/a")),
);
map.module_index.insert(
"B".to_string(),
X86ModuleMapEntry::new("B", Path::new("/b")),
);
let names = map.all_module_names();
assert_eq!(names.len(), 2);
}
#[test]
fn test_module_map_module_headers() {
let mut entry = X86ModuleMapEntry::new("Test", Path::new("/include"));
entry.headers = vec!["test.h".to_string(), "test_extra.h".to_string()];
let mut map = X86ModuleMap::new();
map.module_index.insert("Test".to_string(), entry);
let headers = map.module_headers("Test");
assert_eq!(headers.len(), 2);
}
#[test]
fn test_module_map_module_umbrella() {
let mut entry = X86ModuleMapEntry::new("UmbrellaMod", Path::new("/include"));
entry.umbrella = Some("UmbrellaMod.h".to_string());
let mut map = X86ModuleMap::new();
map.module_index.insert("UmbrellaMod".to_string(), entry);
let umbrella = map.module_umbrella("UmbrellaMod");
assert!(umbrella.is_some());
}
#[test]
fn test_module_map_to_base_module_map() {
let mut map = X86ModuleMap::new();
map.module_index.insert(
"modA".to_string(),
X86ModuleMapEntry::new("modA", Path::new("/a")),
);
map.module_index.insert(
"modB".to_string(),
X86ModuleMapEntry::new("modB", Path::new("/b")),
);
let base = map.to_base_module_map();
assert!(base.has_module("modA"));
assert!(base.has_module("modB"));
}
#[test]
fn test_module_map_write_to_file() {
let map = X86ModuleMap::new();
let tmp = std::env::temp_dir().join("test_x86_modulemap_output");
let result = map.write_to_file(&tmp);
assert!(result.is_ok());
let _ = fs::remove_file(&tmp);
}
#[test]
fn test_dependency_incremental_rebuild() {
let mut dep = X86ModuleDependency::new();
dep.set_status("modA", X86DependencyStatus::Pending);
dep.set_status("modB", X86DependencyStatus::Pending);
dep.add_dependency("modB", "modA");
let rebuild = dep.compute_incremental_rebuild(&["modA".to_string()]);
assert!(rebuild.contains(&"modA".to_string()));
assert!(rebuild.contains(&"modB".to_string()));
}
#[test]
fn test_dependency_record_timestamp() {
let mut dep = X86ModuleDependency::new();
dep.set_status("mod", X86DependencyStatus::Pending);
dep.record_timestamp(
"mod",
X86FileTimestamps {
source_mtime: 1000,
bmi_mtime: 900,
deps_mtime: 800,
source_hash: 0xABCD,
flags_hash: 0x1234,
},
);
dep.needs_full_rebuild = false;
assert!(!dep.needs_rebuild("mod"));
}
#[test]
fn test_dependency_record_change() {
let mut dep = X86ModuleDependency::new();
dep.record_change(X86DependencyChange::Added("newmod".to_string()));
assert_eq!(dep.pending_changes().len(), 1);
dep.clear_pending_changes();
assert!(dep.pending_changes().is_empty());
}
#[test]
fn test_dependency_edge_count() {
let mut dep = X86ModuleDependency::new();
dep.add_dependency("B", "A");
dep.add_dependency("C", "B");
dep.add_dependency("C", "A");
assert_eq!(dep.edge_count(), 3);
}
#[test]
fn test_bmi_transfer_operation_log() {
let mut transfer = X86BMITransfer::new();
assert!(transfer.operation_log().is_empty());
transfer.operation_log.push(X86BMIOperation {
op: X86BMIOpKind::Export,
module_name: "test".to_string(),
file_size: 1024,
success: true,
error: None,
});
assert_eq!(transfer.operation_log().len(), 1);
transfer.clear_log();
assert!(transfer.operation_log().is_empty());
}
#[test]
fn test_bmi_transfer_validate_file() {
let transfer = X86BMITransfer::new();
let result = transfer.validate_bmi_file(Path::new("/nonexistent/bmi.pcm"));
assert!(result.is_err());
}
#[test]
fn test_bmi_transfer_remove_file() {
let mut transfer = X86BMITransfer::new();
let result = transfer.remove_bmi(Path::new("/nonexistent/bmi.pcm"));
assert!(result.is_err());
}
#[test]
fn test_bmi_flags_default() {
let flags = X86BMIFlags::default();
assert!(!flags.sse_enabled);
assert!(!flags.avx_enabled);
assert!(!flags.is_64bit);
}
#[test]
fn test_cache_lookup_disabled() {
let mut cache = X86ModuleCache::new(&ClangOptions::default());
cache.enabled = false;
assert!(cache.lookup("test").is_none());
assert!(cache.lookup_by_hash("test", "abc").is_none());
}
#[test]
fn test_cache_set_root() {
let mut cache = X86ModuleCache::new(&ClangOptions::default());
let new_root = std::env::temp_dir().join("x86_mod_cache_test");
cache.set_cache_root(&new_root);
assert_eq!(cache.cache_root, new_root);
let _ = fs::remove_dir_all(&new_root);
}
#[test]
fn test_cache_verify_empty() {
let cache = X86ModuleCache::new(&ClangOptions::default());
let errors = cache.verify_cache();
assert!(errors.is_empty());
}
#[test]
fn test_ownership_enter_leave_fragments() {
let mut ownership = X86ModuleOwnership::new();
assert!(!ownership.in_global_fragment);
ownership.enter_global_fragment();
assert!(ownership.in_global_fragment);
ownership.leave_global_fragment();
assert!(!ownership.in_global_fragment);
ownership.enter_private_fragment();
assert!(ownership.in_private_fragment);
ownership.leave_private_fragment();
assert!(!ownership.in_private_fragment);
}
#[test]
fn test_ownership_register_visible_and_reachable() {
let mut ownership = X86ModuleOwnership::new();
ownership.register_visible_name("mod", "VisibleType");
ownership.register_reachable_name("mod", "ReachableFunc");
assert!(ownership.is_name_visible("VisibleType", "mod"));
assert!(ownership.is_name_visible("ReachableFunc", "mod"));
assert!(!ownership.is_name_visible("SomethingElse", "mod"));
}
#[test]
fn test_linkage_all_kinds_to_ir() {
assert_eq!(
X86ModuleLinkage::linkage_to_ir_string(X86ModuleLinkageKind::Strong),
"external"
);
assert_eq!(
X86ModuleLinkage::linkage_to_ir_string(X86ModuleLinkageKind::Weak),
"weak"
);
assert_eq!(
X86ModuleLinkage::linkage_to_ir_string(X86ModuleLinkageKind::WeakODR),
"weak_odr"
);
assert_eq!(
X86ModuleLinkage::linkage_to_ir_string(X86ModuleLinkageKind::Internal),
"internal"
);
assert_eq!(
X86ModuleLinkage::linkage_to_ir_string(X86ModuleLinkageKind::LinkOnceODR),
"linkonce_odr"
);
assert_eq!(
X86ModuleLinkage::linkage_to_ir_string(X86ModuleLinkageKind::AvailableExternally),
"available_externally"
);
}
#[test]
fn test_linkage_all_visibilities_to_ir() {
assert_eq!(
X86ModuleLinkage::visibility_to_ir_string(X86SymbolVisibility::Default),
"default"
);
assert_eq!(
X86ModuleLinkage::visibility_to_ir_string(X86SymbolVisibility::Hidden),
"hidden"
);
assert_eq!(
X86ModuleLinkage::visibility_to_ir_string(X86SymbolVisibility::Protected),
"protected"
);
assert_eq!(
X86ModuleLinkage::visibility_to_ir_string(X86SymbolVisibility::Internal),
"internal"
);
}
#[test]
fn test_linkage_remove_override() {
let mut linkage = X86ModuleLinkage::new(&ClangOptions::default());
linkage.set_linkage_override(
"mod",
X86LinkageOverride {
module_name: "mod".to_string(),
linkage: X86ModuleLinkageKind::Strong,
visibility: X86SymbolVisibility::Default,
force_inline: false,
},
);
linkage.remove_linkage_override("mod");
let result = linkage.determine_linkage("func", "mod", true, true);
assert_eq!(result.linkage, X86ModuleLinkageKind::WeakODR);
}
#[test]
fn test_import_allow_unresolved() {
let mut handler = X86ModuleImport::new();
handler.set_allow_unresolved(true);
let import = ModuleImport::new(ModuleName::from_str("nonexistent"));
let map = X86ModuleMap::new();
let cache = X86ModuleCache::new(&ClangOptions::default());
let result = handler.resolve_import(&import, &map, &cache);
assert!(result.is_ok());
}
#[test]
fn test_import_resolved_bmi_path() {
let handler = X86ModuleImport::new();
assert!(handler.resolved_bmi_path("unknown").is_none());
}
#[test]
fn test_import_active_and_resolved() {
let handler = X86ModuleImport::new();
assert!(handler.active_imports().is_empty());
assert!(handler.resolved_imports().is_empty());
}
#[test]
fn test_std_module_new() {
let mut mod_def = X86StdModule::new("custom_std");
mod_def.add_header("<custom>");
mod_def.add_dependency("std.core");
assert_eq!(mod_def.name, "custom_std");
assert_eq!(mod_def.headers.len(), 1);
assert_eq!(mod_def.dependencies.len(), 1);
assert!(!mod_def.is_primary);
assert_eq!(mod_def.min_standard, CppStandard::Cpp20);
}
#[test]
fn test_std_modules_not_std_module() {
let std_mods = X86ModuleStandardLibrary::new();
assert!(!std_mods.is_std_module("boost"));
assert!(!std_mods.is_std_module("mylib"));
assert!(std_mods.is_std_module("std"));
}
#[test]
fn test_std_modules_get_nonexistent() {
let std_mods = X86ModuleStandardLibrary::new();
assert!(std_mods.get_std_module("not_a_std_module").is_none());
}
#[test]
fn test_std_modules_all_names() {
let std_mods = X86ModuleStandardLibrary::new();
let names = std_mods.module_names();
assert!(names.contains(&"std"));
assert!(names.contains(&"std.core"));
assert!(names.contains(&"std.io"));
assert!(names.contains(&"std.containers"));
}
#[test]
fn test_std_modules_containers_deps() {
let std_mods = X86ModuleStandardLibrary::new();
let containers = std_mods.get_std_module("std.containers").unwrap();
assert!(containers.dependencies.contains(&"std.core".to_string()));
assert!(containers.dependencies.contains(&"std.memory".to_string()));
}
#[test]
fn test_std_modules_strings_deps() {
let std_mods = X86ModuleStandardLibrary::new();
let strings = std_mods.get_std_module("std.strings").unwrap();
assert!(strings.dependencies.contains(&"std.core".to_string()));
}
#[test]
fn test_std_modules_numerics_deps() {
let std_mods = X86ModuleStandardLibrary::new();
let numerics = std_mods.get_std_module("std.numerics").unwrap();
assert!(numerics.dependencies.contains(&"std.core".to_string()));
}
#[test]
fn test_std_modules_ranges_deps() {
let std_mods = X86ModuleStandardLibrary::new();
let ranges = std_mods.get_std_module("std.ranges").unwrap();
assert!(ranges.dependencies.contains(&"std.core".to_string()));
assert!(ranges.dependencies.contains(&"std.concepts".to_string()));
}
#[test]
fn test_darwin_module_flags_default() {
let flags = X86DarwinModuleFlags::default();
assert!(!flags.is_framework_bundle);
assert!(flags.framework_version.is_none());
assert!(!flags.use_system_cache);
assert!(!flags.is_system_module);
assert!(!flags.requires_explicit_build);
}
#[test]
fn test_x86_cache_config_default() {
let config = X86CacheConfig::default();
assert!(config.use_triple_subdir);
assert_eq!(config.target_triple, "x86_64-unknown-linux-gnu");
assert!(!config.use_lock_file);
assert!(!config.use_hard_links);
assert!(!config.verify_on_startup);
assert_eq!(config.gc_threshold, 0.8);
}
#[test]
fn test_module_compiler_with_different_output_dir() {
let mut compiler = X86ModuleCompiler::new(ClangOptions::default());
let custom_dir = std::env::temp_dir().join("custom_x86_modules");
compiler.set_output_dir(&custom_dir);
assert_eq!(compiler.output_dir, custom_dir);
}
#[test]
fn test_x86_module_map_entry_full_name() {
let mut entry = X86ModuleMapEntry::new("child", Path::new("/base"));
assert_eq!(entry.fully_qualified_name(), "child");
entry.parent = Some("parent".to_string());
assert_eq!(entry.fully_qualified_name(), "parent.child");
}
#[test]
fn test_x86_module_map_entry_has_submodules() {
let mut entry = X86ModuleMapEntry::new("root", Path::new("/base"));
assert!(!entry.has_submodules());
entry.submodules.push("child".to_string());
assert!(entry.has_submodules());
}
#[test]
fn test_dependency_transitive_reduction() {
let mut dep = X86ModuleDependency::new();
dep.add_dependency("A", "B");
dep.add_dependency("B", "C");
dep.add_dependency("A", "C");
let redundant = dep.transitive_reduction();
assert!(redundant.iter().any(|(a, c)| a == "A" && c == "C"));
}
#[test]
fn test_dependency_self_loop_ignored() {
let mut dep = X86ModuleDependency::new();
dep.add_dependency("A", "A");
assert!(!dep.has_cycles());
}
#[test]
fn test_cache_store_disabled() {
let mut cache = X86ModuleCache::new(&ClangOptions::default());
cache.enabled = false;
let unit = X86CompilationUnit {
source_path: PathBuf::from("test.cppm"),
module_decl: Some(ModuleDecl::interface(ModuleName::from_str("test"))),
unit_kind: X86CompilationUnitKind::ModuleInterface,
target_triple: "x86_64-unknown-linux-gnu".to_string(),
include_paths: vec![],
defines: vec![],
required_imports: vec![],
};
let result = X86CompilationResult::new(
"test",
Some(PathBuf::from("test.pcm")),
false,
X86CompilationUnitKind::ModuleInterface,
);
let store_result = cache.store(&unit, &result);
assert!(store_result.is_ok());
}
#[test]
fn test_ownership_validate_consistency() {
let mut ownership = X86ModuleOwnership::new();
ownership.register_module(
ModuleName::from_str("mod"),
X86CompilationUnitKind::ModuleInterface,
);
ownership.record_decl_ownership(1, "func");
ownership.mark_exported(1);
let errors = ownership.validate();
assert!(errors.is_empty());
}
#[test]
fn test_linkage_validate_no_duplicates() {
let mut linkage = X86ModuleLinkage::new(&ClangOptions::default());
linkage.register_initializer("mod", "init1", 100);
linkage.register_initializer("mod", "init2", 200);
let errors = linkage.validate();
assert!(errors.is_empty());
}
#[test]
fn test_linkage_validate_duplicate_priorities() {
let mut linkage = X86ModuleLinkage::new(&ClangOptions::default());
linkage.register_initializer("mod", "init1", 100);
linkage.register_initializer("mod", "init2", 100);
let errors = linkage.validate();
assert!(!errors.is_empty());
}
#[test]
fn test_import_parse_invalid() {
let handler = X86ModuleImport::new();
let result = handler.parse_import("not an import");
assert!(result.is_none());
}
#[test]
fn test_import_parse_partition_import() {
let handler = X86ModuleImport::new();
let result = handler.parse_import("import mylib:internal;");
assert!(result.is_some());
}
#[test]
fn test_integration_module_map_and_dependency() {
let mut map = X86ModuleMap::new();
map.module_index.insert(
"base".to_string(),
X86ModuleMapEntry::new("base", Path::new("/src")),
);
map.module_index.insert(
"derived".to_string(),
X86ModuleMapEntry::new("derived", Path::new("/src")),
);
let mut deps = X86ModuleDependency::new();
deps.set_status("base", X86DependencyStatus::Compiled);
deps.set_status("derived", X86DependencyStatus::Pending);
deps.add_dependency("derived", "base");
let order = deps.topological_sort();
assert!(order.is_ok());
}
#[test]
fn test_module_map_discover_in_temp() {
let mut map = X86ModuleMap::new();
let tmp_dir = std::env::temp_dir().join("x86_module_map_test_dir");
let _ = fs::create_dir_all(&tmp_dir);
let map_path = tmp_dir.join(X86_MODULE_MAP_FILE);
fs::write(&map_path, "module TestMod { header \"test.h\" }\n").unwrap();
map.add_search_path(&tmp_dir);
let result = map.discover_module_maps();
assert!(result.is_ok());
let _ = fs::remove_dir_all(&tmp_dir);
}
#[test]
fn test_compilation_unit_kind_debug() {
assert_ne!(
X86CompilationUnitKind::ModuleInterface,
X86CompilationUnitKind::ModuleImplementation
);
assert_ne!(
X86CompilationUnitKind::ModulePartitionInterface,
X86CompilationUnitKind::HeaderUnit
);
assert_ne!(
X86CompilationUnitKind::NonModular,
X86CompilationUnitKind::GlobalFragment
);
}
#[test]
fn test_dependency_status_all_variants() {
assert_ne!(X86DependencyStatus::Pending, X86DependencyStatus::Compiled);
assert_ne!(X86DependencyStatus::Failed, X86DependencyStatus::Skipped);
assert_ne!(
X86DependencyStatus::InProgress,
X86DependencyStatus::UpToDate
);
}
#[test]
fn test_bmi_op_kind_all_variants() {
assert_ne!(X86BMIOpKind::Export, X86BMIOpKind::Import);
assert_ne!(X86BMIOpKind::Verify, X86BMIOpKind::Update);
assert_ne!(X86BMIOpKind::Remove, X86BMIOpKind::Export);
}
#[test]
fn test_module_map_entry_x86_config() {
let entry = X86ModuleMapEntry::new("mod", Path::new("/path"));
assert!(entry.x86_config_flags.is_empty());
assert!(!entry.is_extern);
assert!(entry.parent.is_none());
}
#[test]
fn test_module_compiler_default_output_dir() {
let compiler = X86ModuleCompiler::new(ClangOptions::default());
assert_eq!(compiler.output_dir, PathBuf::from(X86_MODULE_CACHE_DIR));
assert!(!compiler.verbose);
}
#[test]
fn test_module_compiler_verbose() {
let mut options = ClangOptions::default();
options.verbose = true;
let compiler = X86ModuleCompiler::new(options);
assert!(compiler.verbose);
}
#[test]
fn test_x86_modules_stat_default() {
let stats = X86ModuleStats::default();
assert_eq!(stats.modules_compiled, 0);
assert_eq!(stats.bmis_imported, 0);
assert_eq!(stats.cache_hits, 0);
}
#[test]
fn test_x86_modules_validate() {
let options = ClangOptions::default();
let target = X86TargetMachine::default();
let modules = X86Modules::new(options, target);
let errors = modules.validate();
assert!(errors.is_empty());
}
#[test]
fn test_x86_modules_add_multiple_units() {
let options = ClangOptions::default();
let target = X86TargetMachine::default();
let mut modules = X86Modules::new(options, target);
let units: Vec<X86CompilationUnit> = (0..5)
.map(|i| X86CompilationUnit {
source_path: PathBuf::from(format!("mod{}.cppm", i)),
module_decl: Some(ModuleDecl::interface(ModuleName::from_str(&format!(
"mod{}",
i
)))),
unit_kind: X86CompilationUnitKind::ModuleInterface,
target_triple: "x86_64-unknown-linux-gnu".to_string(),
include_paths: vec![],
defines: vec![],
required_imports: vec![],
})
.collect();
modules.add_compilation_units(units);
assert_eq!(modules.pending_units.len(), 5);
}
#[test]
fn test_import_include_paths() {
let mut handler = X86ModuleImport::new();
let paths = vec![
PathBuf::from("/usr/include"),
PathBuf::from("/usr/local/include"),
];
handler.set_include_paths(paths);
assert_eq!(handler.system_include_paths.len(), 2);
}
#[test]
fn test_ownership_compute_visible_for_unknown_module() {
let ownership = X86ModuleOwnership::new();
let visible = ownership.compute_visible_names("unknown");
assert!(visible.is_empty());
}
#[test]
fn test_std_modules_threading() {
let std_mods = X86ModuleStandardLibrary::new();
let threading = std_mods.get_std_module("std.threading").unwrap();
assert!(threading.headers.contains(&"<thread>".to_string()));
assert!(threading.headers.contains(&"<mutex>".to_string()));
}
#[test]
fn test_std_modules_concepts() {
let std_mods = X86ModuleStandardLibrary::new();
let concepts = std_mods.get_std_module("std.concepts").unwrap();
assert!(concepts.headers.contains(&"<concepts>".to_string()));
}
#[test]
fn test_cpp_standard_eq() {
assert_eq!(CppStandard::Cpp17, CppStandard::Cpp17);
assert_ne!(CppStandard::Cpp17, CppStandard::Cpp20);
}
#[test]
fn test_compilation_unit_kind_eq() {
assert_eq!(
X86CompilationUnitKind::ModuleInterface,
X86CompilationUnitKind::ModuleInterface
);
assert_ne!(
X86CompilationUnitKind::ModuleInterface,
X86CompilationUnitKind::ModuleImplementation
);
}
#[test]
fn test_bmi_flags_field_assignment() {
let mut flags = X86BMIFlags::default();
flags.sse_enabled = true;
flags.sse2_enabled = true;
flags.is_64bit = true;
assert!(flags.sse_enabled);
assert!(flags.sse2_enabled);
assert!(flags.is_64bit);
}
#[test]
fn test_x86_file_timestamps() {
let ts = X86FileTimestamps {
source_mtime: 1000,
bmi_mtime: 900,
deps_mtime: 800,
source_hash: 0xDEADBEEF,
flags_hash: 0xCAFE,
};
assert_eq!(ts.source_mtime, 1000);
assert_eq!(ts.source_hash, 0xDEADBEEF);
}
#[test]
fn test_dependency_change_variants() {
let change1 = X86DependencyChange::Added("mod".to_string());
let change2 = X86DependencyChange::Removed("mod".to_string());
assert!(matches!(change1, X86DependencyChange::Added(_)));
assert!(matches!(change2, X86DependencyChange::Removed(_)));
let change3 = X86DependencyChange::EdgeAdded("A".to_string(), "B".to_string());
assert!(matches!(change3, X86DependencyChange::EdgeAdded(_, _)));
}
#[test]
fn test_integration_compiler_cache_interaction() {
let options = ClangOptions::default();
let mut compiler = X86ModuleCompiler::new(options.clone());
compiler.set_output_dir(&std::env::temp_dir().join("x86_mod_test_output"));
let module_map = X86ModuleMap::new();
let import_handler = X86ModuleImport::new();
let unit = X86CompilationUnit {
source_path: PathBuf::from("simple.cppm"),
module_decl: Some(ModuleDecl::interface(ModuleName::from_str("simple"))),
unit_kind: X86CompilationUnitKind::ModuleInterface,
target_triple: "x86_64-unknown-linux-gnu".to_string(),
include_paths: vec![],
defines: vec![],
required_imports: vec![],
};
let result = compiler.compile_unit(&unit, &module_map, &import_handler);
assert!(result.is_ok());
let compiled = result.unwrap();
assert_eq!(compiled.module_name, "simple");
assert!(compiled.bmi_path.is_some());
}
#[test]
fn test_module_constants() {
assert_eq!(X86_MODULE_CACHE_DIR, ".x86_module_cache");
assert_eq!(X86_BMI_EXTENSION, ".pcm");
assert_eq!(X86_MODULE_MAP_FILE, "module.modulemap");
assert_eq!(X86_CACHE_MAX_VERSIONS, 5);
assert_eq!(X86_BMI_SIGNATURE_MAGIC, b"X86M");
assert_eq!(X86_BMI_EXTENDED_VERSION, 2);
}
#[test]
fn test_module_map_entry_darwin_flags() {
let flags = X86DarwinModuleFlags {
is_framework_bundle: true,
framework_version: Some("A".to_string()),
use_system_cache: true,
is_system_module: true,
requires_explicit_build: true,
};
assert!(flags.is_framework_bundle);
assert_eq!(flags.framework_version, Some("A".to_string()));
}
#[test]
fn test_cache_config_custom() {
let config = X86CacheConfig {
use_triple_subdir: false,
target_triple: "i386-unknown-linux-gnu".to_string(),
use_lock_file: true,
use_hard_links: true,
verify_on_startup: true,
gc_threshold: 0.5,
};
assert!(!config.use_triple_subdir);
assert_eq!(config.target_triple, "i386-unknown-linux-gnu");
assert!(config.use_lock_file);
}
#[test]
fn test_bmi_operation_log_entry() {
let entry = X86BMIOperation {
op: X86BMIOpKind::Import,
module_name: "test".to_string(),
file_size: 2048,
success: true,
error: None,
};
assert_eq!(entry.op, X86BMIOpKind::Import);
assert!(entry.success);
}
#[test]
fn test_symbol_linkage_fields() {
let sym = X86SymbolLinkage {
symbol_name: "_Z3foov".to_string(),
linkage: X86ModuleLinkageKind::WeakODR,
visibility: X86SymbolVisibility::Default,
owning_module: "mylib".to_string(),
is_definition: true,
};
assert_eq!(sym.symbol_name, "_Z3foov");
assert!(sym.is_definition);
assert_eq!(sym.owning_module, "mylib");
}
#[test]
fn test_module_init_and_fini() {
let init = X86ModuleInit {
module_name: "mod".to_string(),
init_function: "_GLOBAL__I_mod".to_string(),
priority: 65535,
before_main: true,
};
assert_eq!(init.module_name, "mod");
assert!(init.before_main);
let fini = X86ModuleFini {
module_name: "mod".to_string(),
fini_function: "_GLOBAL__D_mod".to_string(),
priority: 65535,
at_exit: true,
};
assert_eq!(fini.module_name, "mod");
assert!(fini.at_exit);
}
#[test]
fn test_linkage_override_fields() {
let ov = X86LinkageOverride {
module_name: "mod".to_string(),
linkage: X86ModuleLinkageKind::LinkOnceODR,
visibility: X86SymbolVisibility::Hidden,
force_inline: true,
};
assert_eq!(ov.module_name, "mod");
assert!(ov.force_inline);
assert_eq!(ov.linkage, X86ModuleLinkageKind::LinkOnceODR);
}
#[test]
fn test_module_map_entry_all_fields() {
let entry = X86ModuleMapEntry {
module_name: "FullMod".to_string(),
umbrella: Some("FullMod.h".to_string()),
headers: vec!["a.h".to_string(), "b.h".to_string()],
excluded_headers: vec!["internal.h".to_string()],
submodules: vec!["sub1".to_string()],
is_framework: false,
is_explicit: true,
exports: vec!["OtherMod".to_string()],
link_libraries: vec!["pthread".to_string()],
base_dir: PathBuf::from("/usr/include"),
is_extern: true,
parent: Some("ParentMod".to_string()),
x86_config_flags: vec!["x86_64".to_string()],
darwin_specific: X86DarwinModuleFlags::default(),
};
assert_eq!(entry.module_name, "FullMod");
assert!(entry.is_extern);
assert!(entry.has_submodules());
assert_eq!(entry.fully_qualified_name(), "ParentMod.FullMod");
}
#[test]
fn test_compilation_result_all_fields() {
let result = X86CompilationResult {
module_name: "test".to_string(),
bmi_path: Some(PathBuf::from("/cache/test.pcm")),
from_cache: true,
unit_kind: X86CompilationUnitKind::ModuleInterface,
};
assert!(result.from_cache);
assert_eq!(result.unit_kind, X86CompilationUnitKind::ModuleInterface);
}
#[test]
fn test_compilation_unit_all_fields() {
let unit = X86CompilationUnit {
source_path: PathBuf::from("/src/test.cppm"),
module_decl: Some(ModuleDecl {
name: ModuleName::from_str("mylib.internal"),
kind: ModuleKind::ModulePartitionImplementation,
partition: Some("internal".to_string()),
source_loc: Some("test.cppm:1:1".to_string()),
}),
unit_kind: X86CompilationUnitKind::ModulePartitionImplementation,
target_triple: "i386-pc-linux-gnu".to_string(),
include_paths: vec![PathBuf::from("/usr/include")],
defines: vec![("DEBUG".to_string(), Some("1".to_string()))],
required_imports: vec![ModuleImport::new(ModuleName::from_str("mylib"))],
};
assert_eq!(unit.target_triple, "i386-pc-linux-gnu");
assert_eq!(unit.defines.len(), 1);
assert_eq!(unit.required_imports.len(), 1);
}
#[test]
fn test_module_stats_all_fields() {
let stats = X86ModuleStats {
modules_compiled: 10,
bmis_imported: 5,
bmis_exported: 3,
cache_hits: 100,
cache_misses: 20,
total_bmi_bytes: 1_048_576,
compilation_time_ms: 5000,
};
assert_eq!(stats.modules_compiled, 10);
assert_eq!(stats.cache_hits, 100);
assert_eq!(stats.total_bmi_bytes, 1_048_576);
}
#[test]
fn test_std_modules_memory() {
let std_mods = X86ModuleStandardLibrary::new();
let memory = std_mods.get_std_module("std.memory").unwrap();
assert!(memory.headers.contains(&"<memory>".to_string()));
assert!(memory.headers.contains(&"<memory_resource>".to_string()));
}
#[test]
fn test_std_modules_expected_cpp23() {
let mut std_mods = X86ModuleStandardLibrary::new();
std_mods.set_cpp_standard(CppStandard::Cpp23);
assert!(std_mods.is_std_module("std.expected"));
assert!(std_mods.is_std_module("std.stacktrace"));
}
#[test]
fn test_end_to_end_module_compilation_flow() {
let options = ClangOptions::default();
let target = X86TargetMachine::default();
let mut modules = X86Modules::new(options, target);
let base = X86CompilationUnit {
source_path: PathBuf::from("base.cppm"),
module_decl: Some(ModuleDecl {
name: ModuleName::from_str("base"),
kind: ModuleKind::ModuleInterface,
partition: None,
source_loc: None,
}),
unit_kind: X86CompilationUnitKind::ModuleInterface,
target_triple: "x86_64-unknown-linux-gnu".to_string(),
include_paths: vec![],
defines: vec![],
required_imports: vec![],
};
let consumer = X86CompilationUnit {
source_path: PathBuf::from("consumer.cppm"),
module_decl: Some(ModuleDecl {
name: ModuleName::from_str("consumer"),
kind: ModuleKind::ModuleInterface,
partition: None,
source_loc: None,
}),
unit_kind: X86CompilationUnitKind::ModuleInterface,
target_triple: "x86_64-unknown-linux-gnu".to_string(),
include_paths: vec![],
defines: vec![],
required_imports: vec![ModuleImport::new(ModuleName::from_str("base"))],
};
modules.add_compilation_unit(base);
modules.add_compilation_unit(consumer);
let result = modules.process_all_units();
assert!(result.is_ok());
let results = result.unwrap();
assert_eq!(results.len(), 2);
assert_eq!(results[0].module_name, "base");
assert!(results[0].bmi_path.is_some());
}
}