use alloc::string::String;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum AmdWmma {
Rdna3,
Rdna4,
}
impl AmdWmma {
pub fn frag_ab_elems(&self, k: usize) -> usize {
match self {
AmdWmma::Rdna3 => k,
AmdWmma::Rdna4 => k / 2,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum AMDArchitecture {
GFX12,
GFX11,
GFX103,
GFX101,
GFX908,
GFX90A,
GFX94,
Other,
}
impl AMDArchitecture {
pub fn wmma_generation(&self) -> Option<AmdWmma> {
match self {
AMDArchitecture::GFX11 => Some(AmdWmma::Rdna3),
AMDArchitecture::GFX12 => Some(AmdWmma::Rdna4),
_ => None,
}
}
pub fn plane_dim(&self) -> Option<u32> {
match self {
AMDArchitecture::GFX101
| AMDArchitecture::GFX103
| AMDArchitecture::GFX11
| AMDArchitecture::GFX12 => Some(32),
AMDArchitecture::GFX908 | AMDArchitecture::GFX90A | AMDArchitecture::GFX94 => Some(64),
AMDArchitecture::Other => None,
}
}
pub fn parse(arg: &str) -> Result<Self, String> {
let norm = arg.to_lowercase();
if norm.starts_with("gfx12") {
Ok(AMDArchitecture::GFX12)
} else if norm.starts_with("gfx11") {
Ok(AMDArchitecture::GFX11)
} else if norm.starts_with("gfx103") {
Ok(AMDArchitecture::GFX103)
} else if norm.starts_with("gfx101") {
Ok(AMDArchitecture::GFX101)
} else if norm == "gfx908" {
Ok(AMDArchitecture::GFX908)
} else if norm == "gfx90a" {
Ok(AMDArchitecture::GFX90A)
} else if norm.starts_with("gfx94") {
Ok(AMDArchitecture::GFX94)
} else {
Ok(AMDArchitecture::Other)
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct GfxArch {
name: String,
family: AMDArchitecture,
}
impl GfxArch {
pub fn parse(reported: &str) -> Self {
let name = reported
.split(':')
.next()
.unwrap_or(reported)
.to_lowercase();
let family = AMDArchitecture::parse(&name).unwrap_or(AMDArchitecture::Other);
Self { name, family }
}
pub fn name(&self) -> &str {
&self.name
}
pub fn family(&self) -> AMDArchitecture {
self.family
}
pub fn plane_dim(&self) -> Option<u32> {
self.family.plane_dim()
}
pub fn wmma(&self) -> Option<AmdWmma> {
self.family.wmma_generation()
}
pub fn isa_version(&self) -> &str {
self.name.strip_prefix("gfx").unwrap_or(&self.name)
}
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::string::ToString;
#[test]
fn the_target_feature_suffix_is_not_part_of_the_name() {
let gfx = GfxArch::parse("gfx1151:xnack-:sramecc+");
assert_eq!(gfx.name(), "gfx1151");
assert_eq!(gfx.isa_version(), "1151");
assert_eq!(gfx.family(), AMDArchitecture::GFX11);
}
#[test]
fn a_device_has_one_wave_width_and_one_wmma() {
for (name, plane_dim, wmma) in [
("gfx1201", Some(32), Some(AmdWmma::Rdna4)),
("gfx1100", Some(32), Some(AmdWmma::Rdna3)),
("gfx1030", Some(32), None),
("gfx1010", Some(32), None),
("gfx90a", Some(64), None),
("gfx942", Some(64), None),
] {
let gfx = GfxArch::parse(name);
assert_eq!(gfx.plane_dim(), plane_dim, "{name}");
assert_eq!(gfx.wmma(), wmma, "{name}");
}
}
#[test]
fn rdna1_is_not_rdna2() {
assert_eq!(GfxArch::parse("gfx1010").family(), AMDArchitecture::GFX101);
assert_eq!(GfxArch::parse("gfx1012").family(), AMDArchitecture::GFX101);
assert_eq!(GfxArch::parse("gfx1030").family(), AMDArchitecture::GFX103);
assert_eq!(GfxArch::parse("gfx1032").family(), AMDArchitecture::GFX103);
assert_eq!(GfxArch::parse("gfx1010").plane_dim(), Some(32));
assert_eq!(GfxArch::parse("gfx1030").plane_dim(), Some(32));
assert_eq!(GfxArch::parse("gfx1010").wmma(), None);
}
#[test]
fn an_unknown_architecture_claims_no_wave_width() {
let gfx = GfxArch::parse("gfx1337");
assert_eq!(gfx.family(), AMDArchitecture::Other);
assert_eq!(gfx.plane_dim(), None);
assert_eq!(gfx.name().to_string(), "gfx1337");
}
}