use std::borrow::Cow;
use std::collections::BTreeMap;
use std::sync::LazyLock;
use crate::dialect_lookup::Signature;
use crate::ir::{BufferAccess, Program};
use crate::program_caps::{scan as scan_capabilities, RequiredCapabilities};
pub type OperationFixtures = fn() -> Vec<Vec<Vec<u8>>>;
#[derive(Clone, Copy, Debug)]
pub struct SemanticOperation {
pub id: &'static str,
pub semantic_version: u32,
pub signature: Option<&'static Signature>,
pub tier: OperationTier,
pub category: Option<&'static str>,
pub build: Option<fn() -> Program>,
pub test_inputs: Option<OperationFixtures>,
pub expected_output: Option<OperationFixtures>,
pub laws: &'static [&'static str],
pub tolerance: TolerancePolicy,
}
impl SemanticOperation {
#[must_use]
pub fn program(self) -> Option<Program> {
self.build.map(|build| build().with_entry_op_id(self.id))
}
#[must_use]
pub fn required_capabilities(self) -> Option<RequiredCapabilities> {
self.program().map(|program| scan_capabilities(&program))
}
#[must_use]
pub fn effects(self) -> Option<OperationEffects> {
self.program()
.map(|program| OperationEffects::from_program(&program))
}
#[must_use]
pub const fn category(self) -> Option<&'static str> {
self.category
}
#[must_use]
pub const fn tolerance(self) -> u32 {
self.tolerance.f32_ulp
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum OperationTier {
Foundation,
Intrinsic,
Primitive,
Library,
Runtime,
External,
Unknown,
}
impl OperationTier {
#[must_use]
pub const fn matrix_value(self) -> &'static str {
match self {
Self::Foundation => "foundation_ir",
Self::Intrinsic => "intrinsic",
Self::Primitive => "primitive",
Self::Library => "libs",
Self::Runtime => "runtime",
Self::External => "external",
Self::Unknown => "unknown",
}
}
}
#[must_use]
pub fn classify_operation_id(id: &str) -> OperationTier {
if id.starts_with("vyre-intrinsics::hardware::") {
OperationTier::Intrinsic
} else if id.starts_with("vyre-primitives::") {
OperationTier::Primitive
} else if id.starts_with("vyre-libs::") {
OperationTier::Library
} else if id.starts_with("core.") || id.starts_with("io.") || id.starts_with("mem.") {
OperationTier::Runtime
} else if id
.split_once("::")
.is_some_and(|(crate_name, _)| !crate_name.is_empty() && !crate_name.starts_with("vyre-"))
{
OperationTier::External
} else {
OperationTier::Unknown
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub struct OperationEffects {
pub reads: bool,
pub writes: bool,
pub atomics: bool,
pub synchronizes: bool,
}
impl OperationEffects {
#[must_use]
pub fn from_program(program: &Program) -> Self {
let mut effects = Self::default();
for buffer in program.buffers() {
match buffer.access() {
BufferAccess::ReadOnly => effects.reads = true,
BufferAccess::ReadWrite => {
effects.reads = true;
effects.writes = true;
}
BufferAccess::WriteOnly => effects.writes = true,
_ => {
effects.reads = true;
effects.writes = true;
}
}
}
let stats = program.stats();
effects.atomics = stats.atomic_op_count > 0;
effects.synchronizes = stats.has_node_barrier() || stats.distributed_collectives();
effects
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub struct TolerancePolicy {
pub f32_ulp: u32,
}
impl TolerancePolicy {
pub const EXACT: Self = Self { f32_ulp: 0 };
#[must_use]
pub const fn f32_ulp(maximum: u32) -> Self {
Self { f32_ulp: maximum }
}
}
pub struct OperationRegistration {
pub id: &'static str,
pub semantic_version: u32,
pub signature: Option<Signature>,
pub tier: OperationTier,
pub category: Option<&'static str>,
pub build: Option<fn() -> Program>,
pub test_inputs: Option<OperationFixtures>,
pub expected_output: Option<OperationFixtures>,
pub laws: &'static [&'static str],
pub tolerance: TolerancePolicy,
}
impl OperationRegistration {
#[must_use]
pub const fn new(
id: &'static str,
tier: OperationTier,
build: Option<fn() -> Program>,
test_inputs: Option<OperationFixtures>,
expected_output: Option<OperationFixtures>,
) -> Self {
Self {
id,
semantic_version: 1,
signature: None,
tier,
category: None,
build,
test_inputs,
expected_output,
laws: &[],
tolerance: TolerancePolicy::EXACT,
}
}
#[must_use]
pub const fn library(
id: &'static str,
build: fn() -> Program,
test_inputs: Option<OperationFixtures>,
expected_output: Option<OperationFixtures>,
) -> Self {
Self::new(
id,
OperationTier::Library,
Some(build),
test_inputs,
expected_output,
)
}
#[must_use]
pub const fn primitive(
id: &'static str,
build: fn() -> Program,
test_inputs: Option<OperationFixtures>,
expected_output: Option<OperationFixtures>,
) -> Self {
Self::new(
id,
OperationTier::Primitive,
Some(build),
test_inputs,
expected_output,
)
}
#[must_use]
pub const fn with_signature(mut self, signature: Signature) -> Self {
self.signature = Some(signature);
self
}
#[must_use]
pub const fn with_category(mut self, category: &'static str) -> Self {
self.category = Some(category);
self
}
#[must_use]
pub const fn with_laws(mut self, laws: &'static [&'static str]) -> Self {
self.laws = laws;
self
}
#[must_use]
pub const fn category(&self) -> Option<&'static str> {
self.category
}
#[must_use]
pub const fn tolerance(&self) -> u32 {
self.tolerance.f32_ulp
}
#[must_use]
pub const fn with_tolerance(mut self, tolerance: TolerancePolicy) -> Self {
self.tolerance = tolerance;
self
}
#[must_use]
pub fn program(&self) -> Option<Program> {
self.build.map(|build| build().with_entry_op_id(self.id))
}
#[must_use]
pub fn required_capabilities(&self) -> Option<RequiredCapabilities> {
self.program().map(|program| scan_capabilities(&program))
}
#[must_use]
pub fn effects(&self) -> Option<OperationEffects> {
self.program()
.map(|program| OperationEffects::from_program(&program))
}
}
impl From<&'static OperationRegistration> for SemanticOperation {
fn from(registration: &'static OperationRegistration) -> Self {
Self {
id: registration.id,
semantic_version: registration.semantic_version,
signature: registration.signature.as_ref(),
tier: registration.tier,
category: registration.category,
build: registration.build,
test_inputs: registration.test_inputs,
expected_output: registration.expected_output,
laws: registration.laws,
tolerance: registration.tolerance,
}
}
}
inventory::collect!(OperationRegistration);
#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)]
pub enum OperationRegistryError {
#[error("duplicate operation registration `{id}`; keep exactly one semantic owner")]
DuplicateId {
id: &'static str,
},
#[error("operation `{id}` uses semantic version zero; use a positive schema version")]
InvalidVersion {
id: &'static str,
},
#[error("operation `{id}` supplies neither a neutral program nor an explicit signature")]
MissingSemantics {
id: &'static str,
},
#[error(
"operation `{id}` declares tier {declared:?}, but its canonical namespace classifies as {classified:?}"
)]
InvalidTier {
id: &'static str,
declared: OperationTier,
classified: OperationTier,
},
}
pub struct OperationRegistry {
ordered: Vec<&'static OperationRegistration>,
by_id: BTreeMap<&'static str, &'static OperationRegistration>,
}
impl OperationRegistry {
fn build() -> Result<Self, OperationRegistryError> {
let mut ordered = inventory::iter::<OperationRegistration>
.into_iter()
.collect::<Vec<_>>();
ordered.sort_unstable_by_key(|entry| entry.id);
let mut by_id = BTreeMap::new();
for entry in &ordered {
if entry.semantic_version == 0 {
return Err(OperationRegistryError::InvalidVersion { id: entry.id });
}
if entry.build.is_none() && entry.signature.is_none() {
return Err(OperationRegistryError::MissingSemantics { id: entry.id });
}
let classified = classify_operation_id(entry.id);
if classified == OperationTier::Unknown || classified != entry.tier {
return Err(OperationRegistryError::InvalidTier {
id: entry.id,
declared: entry.tier,
classified,
});
}
if by_id.insert(entry.id, *entry).is_some() {
return Err(OperationRegistryError::DuplicateId { id: entry.id });
}
}
Ok(Self { ordered, by_id })
}
#[must_use]
pub fn global() -> &'static Self {
static REGISTRY: LazyLock<OperationRegistry> = LazyLock::new(|| {
OperationRegistry::build()
.unwrap_or_else(|error| panic!("invalid semantic operation registry: {error}"))
});
®ISTRY
}
#[must_use]
pub fn get(&self, id: &str) -> Option<SemanticOperation> {
self.by_id.get(id).copied().map(SemanticOperation::from)
}
pub fn iter(&self) -> impl ExactSizeIterator<Item = SemanticOperation> + '_ {
self.ordered.iter().copied().map(SemanticOperation::from)
}
}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct TargetId(Cow<'static, str>);
impl TargetId {
pub const fn new(id: &'static str) -> Result<Self, &'static str> {
if id.is_empty() || has_surrounding_ascii_whitespace(id.as_bytes()) {
return Err("target identity must be non-empty and contain no surrounding whitespace");
}
Ok(Self(Cow::Borrowed(id)))
}
pub fn from_owned(id: String) -> Result<Self, &'static str> {
if id.is_empty() || has_surrounding_ascii_whitespace(id.as_bytes()) {
return Err("target identity must be non-empty and contain no surrounding whitespace");
}
Ok(Self(Cow::Owned(id)))
}
#[must_use]
pub fn as_str(&self) -> &str {
self.0.as_ref()
}
#[must_use]
pub const fn expect_valid(id: &'static str) -> Self {
if id.is_empty() || has_surrounding_ascii_whitespace(id.as_bytes()) {
panic!("target identity must be non-empty and contain no surrounding whitespace");
}
Self(Cow::Borrowed(id))
}
}
impl serde::Serialize for TargetId {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(self.as_str())
}
}
impl<'de> serde::Deserialize<'de> for TargetId {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let id = <String as serde::Deserialize>::deserialize(deserializer)?;
Self::from_owned(id).map_err(serde::de::Error::custom)
}
}
impl std::fmt::Display for TargetId {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter.write_str(self.as_str())
}
}
impl PartialEq<&str> for TargetId {
fn eq(&self, other: &&str) -> bool {
self.as_str() == *other
}
}
const fn has_surrounding_ascii_whitespace(bytes: &[u8]) -> bool {
matches!(bytes.first(), Some(byte) if byte.is_ascii_whitespace())
|| matches!(bytes.last(), Some(byte) if byte.is_ascii_whitespace())
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TargetOperationFacet {
pub operation_id: &'static str,
pub target_id: TargetId,
pub version: u32,
}