use super::FabricClass;
pub use super::cuda::{MUTANTS, mutate};
pub const SOURCE: &str = super::cuda::SOURCE;
pub const IMPLEMENTATION: &str = "hip-lanes64";
pub const HOST_LAYOUT: u32 = 1;
pub const BLOCK: u32 = 256;
pub const GRID_MAX: u32 = 4096;
pub const COMPILE_OPTIONS: [&str; 2] = ["-std=c++17", "-ffp-contract=off"];
const DOMAIN_IMPLEMENTATION: &[u8] = b"wai:quantum-fabric-hip\x01";
const DOMAIN_MEMBER: &[u8] = b"wai:quantum-fabric-hip-member\x01";
fn hex(bytes: &[u8]) -> String {
bytes.iter().map(|b| format!("{b:02x}")).collect()
}
fn put(o: &mut Vec<u8>, bytes: &[u8]) {
o.extend_from_slice(&(bytes.len() as u64).to_be_bytes());
o.extend_from_slice(bytes);
}
pub fn implementation_id() -> String {
implementation_id_of(SOURCE)
}
pub fn implementation_id_of(source: &str) -> String {
let mut o = DOMAIN_IMPLEMENTATION.to_vec();
put(&mut o, source.as_bytes());
for option in COMPILE_OPTIONS {
put(&mut o, option.as_bytes());
}
o.extend_from_slice(&HOST_LAYOUT.to_be_bytes());
format!("{IMPLEMENTATION}/{}", hex(&blake3::hash(&o).as_bytes()[..8]))
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub struct DeviceIdentity {
pub name: String,
pub uuid: String,
pub arch: String,
pub pci: String,
pub runtime: u32,
pub compiler: (u32, u32),
pub compute_units: u32,
pub memory: u64,
}
impl DeviceIdentity {
pub fn class(&self) -> FabricClass {
FabricClass::Gpu
}
pub fn arch_name(&self) -> &str {
self.arch.split(':').next().unwrap_or("")
}
pub fn arch_option(&self) -> String {
format!("--offload-arch={}", self.arch)
}
pub fn member_id(&self) -> String {
let mut o = DOMAIN_MEMBER.to_vec();
put(&mut o, self.name.as_bytes());
put(&mut o, self.uuid.as_bytes());
put(&mut o, self.arch.as_bytes());
put(&mut o, self.pci.as_bytes());
o.extend_from_slice(&self.runtime.to_be_bytes());
o.extend_from_slice(&self.compiler.0.to_be_bytes());
o.extend_from_slice(&self.compiler.1.to_be_bytes());
o.extend_from_slice(&self.compute_units.to_be_bytes());
o.extend_from_slice(&self.memory.to_be_bytes());
format!("local/gpu-hip-{}", hex(&blake3::hash(&o).as_bytes()[..6]))
}
pub fn key(&self) -> String {
format!("gpu-hip-{}", self.arch_name())
}
}
#[cfg(test)]
mod tests {
use super::*;
fn identity() -> DeviceIdentity {
DeviceIdentity {
name: "a device".into(),
uuid: "00112233445566778899aabbccddeeff".into(),
arch: "gfx1151".into(),
pci: "0000:c2:00".into(),
runtime: 70_152_801,
compiler: (9, 0),
compute_units: 20,
memory: 64 << 30,
}
}
#[test]
fn every_field_of_the_identity_is_in_the_member_id() {
let base = identity();
let id = base.member_id();
assert!(id.starts_with("local/gpu-hip-") && id.len() == "local/gpu-hip-".len() + 12, "{id}");
assert!(id.as_str() > "local/cpu-x86_64" && id.as_str() > "local/cpu-aarch64", "sorts after every CPU reference");
let variants = [
DeviceIdentity { name: "another".into(), ..identity() },
DeviceIdentity { uuid: "ff".into(), ..identity() },
DeviceIdentity { arch: "gfx1100".into(), ..identity() },
DeviceIdentity { arch: "gfx1151:xnack-".into(), ..identity() },
DeviceIdentity { pci: "0000:c3:00".into(), ..identity() },
DeviceIdentity { runtime: 60_000_000, ..identity() },
DeviceIdentity { compiler: (10, 0), ..identity() },
DeviceIdentity { compiler: (9, 1), ..identity() },
DeviceIdentity { compute_units: 40, ..identity() },
DeviceIdentity { memory: 8 << 30, ..identity() },
];
for v in variants {
assert_ne!(v.member_id(), id, "{v:?}");
}
assert_eq!(base.key(), "gpu-hip-gfx1151");
assert_eq!(base.arch_option(), "--offload-arch=gfx1151");
assert_eq!(base.class(), FabricClass::Gpu);
let featured = DeviceIdentity { arch: "gfx90a:sramecc+:xnack-".into(), ..identity() };
assert_eq!((featured.arch_name(), featured.key().as_str()), ("gfx90a", "gpu-hip-gfx90a"));
assert_eq!(featured.arch_option(), "--offload-arch=gfx90a:sramecc+:xnack-");
}
#[test]
fn the_implementation_id_follows_the_kernel_text_and_is_not_the_cuda_one() {
let id = implementation_id();
assert!(id.starts_with("hip-lanes64/") && id.len() == "hip-lanes64/".len() + 16, "{id}");
assert_eq!(SOURCE, super::super::cuda::SOURCE);
assert_ne!(id.split('/').nth(1), super::super::cuda::implementation_id().split('/').nth(1));
for m in &MUTANTS {
assert_ne!(implementation_id_of(&mutate(m)), id, "mutant {}", m.name);
}
}
}