use std::collections::HashMap;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum AmdgpuIsaVersion {
GFX600,
GFX700,
GFX800,
GFX803,
GFX900,
GFX906,
GFX908,
GFX90A,
GFX1010,
GFX1030,
GFX1100,
GFX1150,
}
impl AmdgpuIsaVersion {
pub fn from_str(s: &str) -> Option<Self> {
match s.to_lowercase().as_str() {
"gfx600" => Some(Self::GFX600),
"gfx700" => Some(Self::GFX700),
"gfx800" | "gfx802" => Some(Self::GFX800),
"gfx803" => Some(Self::GFX803),
"gfx900" => Some(Self::GFX900),
"gfx906" => Some(Self::GFX906),
"gfx908" => Some(Self::GFX908),
"gfx90a" => Some(Self::GFX90A),
"gfx1010" => Some(Self::GFX1010),
"gfx1030" => Some(Self::GFX1030),
"gfx1100" => Some(Self::GFX1100),
"gfx1150" => Some(Self::GFX1150),
_ => None,
}
}
pub fn as_str(&self) -> &'static str {
match self {
Self::GFX600 => "gfx600",
Self::GFX700 => "gfx700",
Self::GFX800 => "gfx802",
Self::GFX803 => "gfx803",
Self::GFX900 => "gfx900",
Self::GFX906 => "gfx906",
Self::GFX908 => "gfx908",
Self::GFX90A => "gfx90a",
Self::GFX1010 => "gfx1010",
Self::GFX1030 => "gfx1030",
Self::GFX1100 => "gfx1100",
Self::GFX1150 => "gfx1150",
}
}
pub fn generation(&self) -> AmdgpuGeneration {
match self {
Self::GFX600 | Self::GFX700 => AmdgpuGeneration::GCN1,
Self::GFX800 | Self::GFX803 => AmdgpuGeneration::GCN2,
Self::GFX900 | Self::GFX906 | Self::GFX908 | Self::GFX90A => AmdgpuGeneration::GCN5,
Self::GFX1010 | Self::GFX1030 => AmdgpuGeneration::RDNA1,
Self::GFX1100 | Self::GFX1150 => AmdgpuGeneration::RDNA3,
}
}
pub fn supports_wave32(&self) -> bool {
self.generation() >= AmdgpuGeneration::RDNA1
}
pub fn has_agprs(&self) -> bool {
*self >= Self::GFX908
}
pub fn has_packed_fp16(&self) -> bool {
*self >= Self::GFX900
}
pub fn max_sgprs(&self) -> u32 {
match self {
Self::GFX600 | Self::GFX700 => 104,
Self::GFX800 | Self::GFX803 => 104,
_ => 104,
}
}
pub fn max_vgprs(&self) -> u32 {
if self.supports_wave32() {
256
} else {
256
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum AmdgpuGeneration {
GCN1,
GCN2,
GCN3,
GCN5,
RDNA1,
RDNA2,
RDNA3,
}
#[derive(Debug, Clone)]
pub struct AmdgpuSubtargetFeatures {
pub isa_version: AmdgpuIsaVersion,
pub features: HashMap<String, bool>,
}
impl AmdgpuSubtargetFeatures {
pub fn new(isa: AmdgpuIsaVersion) -> Self {
let mut features = HashMap::new();
features.insert("s-memrealtime".into(), true);
features.insert("s-memtime-inst".into(), isa >= AmdgpuIsaVersion::GFX800);
features.insert("flat-for-global".into(), isa >= AmdgpuIsaVersion::GFX700);
features.insert("flat-scratch".into(), isa >= AmdgpuIsaVersion::GFX803);
features.insert("xnack".into(), false);
features.insert("sram-ecc".into(), false);
features.insert("wavefrontsize64".into(), !isa.supports_wave32());
features.insert("wavefrontsize32".into(), isa.supports_wave32());
features.insert("cumode".into(), false);
features.insert(
"architected-flat-scratch".into(),
isa >= AmdgpuIsaVersion::GFX1010,
);
features.insert("packed-fp16".into(), isa.has_packed_fp16());
features.insert("agprs".into(), isa.has_agprs());
features.insert("dot2-insts".into(), isa >= AmdgpuIsaVersion::GFX906);
features.insert("dot7-insts".into(), isa >= AmdgpuIsaVersion::GFX1010);
features.insert("dot8-insts".into(), isa >= AmdgpuIsaVersion::GFX1030);
Self {
isa_version: isa,
features,
}
}
pub fn has(&self, feature: &str) -> bool {
self.features.get(feature).copied().unwrap_or(false)
}
pub fn set(&mut self, feature: &str, enabled: bool) {
self.features.insert(feature.to_string(), enabled);
}
pub fn wavefront_size(&self) -> u32 {
if self.has("wavefrontsize32") {
32
} else {
64
}
}
pub fn triple_string(&self) -> String {
format!("amdgcn-amd-amdhsa-{}", self.isa_version.as_str())
}
pub fn parse_feature_string(&mut self, s: &str) {
for part in s.split(',') {
let part = part.trim();
if part.is_empty() {
continue;
}
if let Some(feat) = part.strip_prefix('+') {
self.features.insert(feat.to_string(), true);
} else if let Some(feat) = part.strip_prefix('-') {
self.features.insert(feat.to_string(), false);
}
}
}
}
#[derive(Debug, Clone)]
pub struct AmdgpuTargetMachine {
pub isa_version: AmdgpuIsaVersion,
pub features: AmdgpuSubtargetFeatures,
pub cpu: String,
pub triple: String,
}
impl AmdgpuTargetMachine {
pub fn new(isa: AmdgpuIsaVersion) -> Self {
let features = AmdgpuSubtargetFeatures::new(isa);
let triple = features.triple_string();
Self {
isa_version: isa,
features,
cpu: isa.as_str().to_string(),
triple,
}
}
pub fn from_cpu(cpu: &str) -> Option<Self> {
AmdgpuIsaVersion::from_str(cpu).map(Self::new)
}
pub fn feature_string(&self) -> String {
let mut parts = Vec::new();
for (feat, enabled) in &self.features.features {
if *enabled {
parts.push(format!("+{}", feat));
} else {
parts.push(format!("-{}", feat));
}
}
parts.join(",")
}
pub fn is_gcn(&self) -> bool {
matches!(
self.isa_version.generation(),
AmdgpuGeneration::GCN1
| AmdgpuGeneration::GCN2
| AmdgpuGeneration::GCN3
| AmdgpuGeneration::GCN5
)
}
pub fn is_rdna(&self) -> bool {
matches!(
self.isa_version.generation(),
AmdgpuGeneration::RDNA1 | AmdgpuGeneration::RDNA2 | AmdgpuGeneration::RDNA3
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_isa_parse_gfx900() {
let isa = AmdgpuIsaVersion::from_str("gfx900").unwrap();
assert_eq!(isa, AmdgpuIsaVersion::GFX900);
assert_eq!(isa.as_str(), "gfx900");
}
#[test]
fn test_isa_parse_gfx1030() {
let isa = AmdgpuIsaVersion::from_str("gfx1030").unwrap();
assert_eq!(isa, AmdgpuIsaVersion::GFX1030);
assert!(isa.supports_wave32());
}
#[test]
fn test_isa_generation() {
assert_eq!(
AmdgpuIsaVersion::GFX900.generation(),
AmdgpuGeneration::GCN5
);
assert_eq!(
AmdgpuIsaVersion::GFX1010.generation(),
AmdgpuGeneration::RDNA1
);
}
#[test]
fn test_subtarget_features_defaults() {
let feats = AmdgpuSubtargetFeatures::new(AmdgpuIsaVersion::GFX900);
assert!(feats.has("packed-fp16"));
assert!(!feats.has("xnack"));
assert_eq!(feats.wavefront_size(), 64);
}
#[test]
fn test_subtarget_features_wave32() {
let feats = AmdgpuSubtargetFeatures::new(AmdgpuIsaVersion::GFX1010);
assert!(feats.has("wavefrontsize32"));
assert!(feats.has("architected-flat-scratch"));
}
#[test]
fn test_subtarget_parse_feature_string() {
let mut feats = AmdgpuSubtargetFeatures::new(AmdgpuIsaVersion::GFX900);
feats.parse_feature_string("+xnack,-packed-fp16");
assert!(feats.has("xnack"));
assert!(!feats.has("packed-fp16"));
}
#[test]
fn test_target_machine_create() {
let tm = AmdgpuTargetMachine::new(AmdgpuIsaVersion::GFX900);
assert!(tm.is_gcn());
assert!(!tm.is_rdna());
assert!(tm.triple.contains("amdgcn"));
}
#[test]
fn test_target_machine_from_cpu() {
let tm = AmdgpuTargetMachine::from_cpu("gfx1010").unwrap();
assert!(tm.is_rdna());
}
}