use crate::cpu::detect::target_architecture_uname;
use crate::schema::{Compiler, CompilerSet};
use itertools::Itertools;
use std::collections::{HashMap, HashSet};
use std::fmt::{Debug, Formatter};
use std::iter;
use std::sync::{Arc, OnceLock};
pub struct Microarchitecture {
pub(crate) name: String,
pub(crate) parents: Vec<Arc<Microarchitecture>>,
pub(crate) vendor: String,
pub(crate) features: HashSet<String>,
pub(crate) compilers: HashMap<String, Vec<Compiler>>,
pub(crate) generation: usize,
pub(crate) cpu_part: Option<String>,
pub(crate) ancestors: OnceLock<Vec<Arc<Microarchitecture>>>,
}
impl PartialEq<Self> for Microarchitecture {
fn eq(&self, other: &Self) -> bool {
self.name == other.name
&& self.vendor == other.vendor
&& self.features == other.features
&& self.parents == other.parents
&& self.compilers == other.compilers
&& self.generation == other.generation
&& self.cpu_part == other.cpu_part
}
}
impl Eq for Microarchitecture {}
impl Debug for Microarchitecture {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Microarchitecture")
.field("name", &self.name)
.field(
"ancestors",
&self
.ancestors()
.iter()
.map(|arch| arch.name.as_str())
.collect_vec(),
)
.field("vendor", &self.vendor)
.field("features", &self.all_features())
.field("compilers", &self.compilers)
.field("generation", &self.generation)
.field("cpu_part", &self.cpu_part)
.finish()
}
}
impl Microarchitecture {
pub(crate) fn new(
name: String,
parents: Vec<Arc<Microarchitecture>>,
vendor: String,
features: HashSet<String>,
compilers: HashMap<String, Vec<Compiler>>,
) -> Self {
Microarchitecture::new_generation(name, parents, vendor, features, compilers, 0)
}
pub(crate) fn new_generation(
name: String,
parents: Vec<Arc<Microarchitecture>>,
vendor: String,
features: HashSet<String>,
compilers: HashMap<String, Vec<Compiler>>,
generation: usize,
) -> Self {
Microarchitecture {
name,
parents,
vendor,
features,
compilers,
generation,
cpu_part: None,
ancestors: OnceLock::new(),
}
}
pub fn generic(name: &str) -> Microarchitecture {
Microarchitecture::new(
name.to_string(),
vec![],
"generic".to_string(),
HashSet::new(),
HashMap::new(),
)
}
pub fn name(&self) -> &str {
&self.name
}
pub fn vendor(&self) -> &str {
&self.vendor
}
pub fn generation(&self) -> usize {
self.generation
}
pub fn known_targets() -> &'static HashMap<String, Arc<Microarchitecture>> {
static KNOWN_TARGETS: std::sync::OnceLock<HashMap<String, Arc<Microarchitecture>>> =
std::sync::OnceLock::new();
KNOWN_TARGETS.get_or_init(known_microarchitectures)
}
pub fn ancestors(&self) -> &[Arc<Microarchitecture>] {
self.ancestors.get_or_init(|| {
let mut v = self.parents.clone();
for parent in &self.parents {
let new_ancestors = parent
.ancestors()
.iter()
.filter(|a| !v.contains(a))
.cloned()
.collect_vec();
v.extend(new_ancestors);
}
v
})
}
pub fn decendent_of(&self, parent: &Microarchitecture) -> bool {
for p in self.parents.iter() {
if p.as_ref() == parent || p.decendent_of(parent) {
return true;
}
}
false
}
pub fn is_strict_superset(&self, other: &Microarchitecture) -> bool {
self.is_superset(other) && self.name != other.name
}
fn is_superset(&self, other: &Microarchitecture) -> bool {
let a = self.node_set();
let b = other.node_set();
a.is_superset(&b)
}
fn node_set(&self) -> HashSet<&str> {
iter::once(self.name.as_str())
.chain(self.ancestors().iter().map(|a| a.name.as_str()))
.collect()
}
pub fn family(&self) -> &Self {
match self.parents.first() {
Some(parent) => parent.family(),
None => self,
}
}
pub fn all_features(&self) -> HashSet<String> {
let mut features = self.features.clone();
for parent in &self.parents {
features.extend(parent.all_features());
}
features
}
}
#[derive(Debug)]
pub struct UnsupportedMicroarchitecture;
fn known_microarchitectures() -> HashMap<String, Arc<Microarchitecture>> {
let mut known_targets: HashMap<String, Arc<Microarchitecture>> = HashMap::new();
let schema = crate::schema::MicroarchitecturesSchema::schema();
fn fill_target_from_map(
name: &str,
schema: &crate::schema::MicroarchitecturesSchema,
targets: &mut HashMap<String, Arc<Microarchitecture>>,
) {
let data = &schema.microarchitectures;
let values = &data[name];
let parent_names = &values.from;
for parent in parent_names {
if !targets.contains_key(parent) {
fill_target_from_map(parent, schema, targets);
}
}
let parents = parent_names
.iter()
.map(|parent| targets[parent].clone())
.collect::<Vec<Arc<Microarchitecture>>>();
let vendor = values.vendor.clone();
let features: HashSet<String> = values.features.iter().cloned().collect();
let compilers: HashMap<String, Vec<Compiler>> = values
.compilers
.as_ref()
.map(|compilers| {
compilers
.iter()
.map(|(vendor, set)| {
(
vendor.clone(),
match set {
CompilerSet::Several(cs) => cs.clone(),
CompilerSet::Single(c) => vec![c.clone()],
},
)
})
.collect()
})
.unwrap_or_default();
let generation = values.generation.unwrap_or(0);
let cpu_part = values.cpupart.clone();
targets.insert(
name.to_string(),
Arc::new(Microarchitecture {
name: name.to_string(),
parents,
vendor,
features,
compilers,
generation,
cpu_part,
ancestors: OnceLock::new(),
}),
);
}
for name in schema.microarchitectures.keys() {
if !known_targets.contains_key(name) {
fill_target_from_map(name, schema, &mut known_targets);
}
}
if let Ok(host_platform) = target_architecture_uname() {
known_targets
.entry(host_platform.to_string())
.or_insert_with(|| Arc::new(Microarchitecture::generic(&host_platform)));
}
known_targets
}