Skip to main content

hrx/
target.rs

1use crate::{Error, Result};
2use std::ffi::{CStr, CString};
3
4/// The native target family used for executable loading.
5pub const TARGET_FAMILY: &CStr = c"amdgpu";
6/// Default compiler target; devices report their architecture at runtime.
7pub const TARGET_KEY: &str = "gfx1151";
8
9/// An AMDGPU architecture key shared by devices, manifests and compiler profiles.
10///
11/// This is the *profile* target: what a device reports and what the compiler
12/// emits for. It is deliberately narrower than the target a Loom source file
13/// names in `amdgpu.target<...>`, which accepts generic families such as
14/// `gfx11-generic`. The two are chosen independently — generic source compiles
15/// under a bare profile — so this type rejects generic names rather than
16/// silently accepting one a device can never report.
17#[derive(Clone, Debug, PartialEq, Eq)]
18pub struct Target(CString);
19impl Target {
20    /// Validate an architecture key, for example gfx1100, gfx1151 or gfx942.
21    /// Availability is determined by the selected native runtime and compiler.
22    /// Keys must be bare architecture names, without feature suffixes.
23    pub fn new(key: &str) -> Result<Self> {
24        if key.contains(':') {
25            return Err(Error::Message(format!(
26                "expected a bare AMDGPU target, found feature-suffixed name {key:?}"
27            )));
28        }
29        let suffix = key.strip_prefix("gfx").unwrap_or("");
30        if !(3..=4).contains(&suffix.len())
31            || !suffix
32                .bytes()
33                .all(|b| b.is_ascii_digit() || (b'a'..=b'f').contains(&b))
34        {
35            return Err(Error::Message(format!("invalid AMDGPU target {key:?}")));
36        }
37        Ok(Self(
38            CString::new(key).expect("validated target has no NUL"),
39        ))
40    }
41    // Device properties may include features such as :sramecc+:xnack-. Profiles
42    // and executable loading take the base architecture; features are not configured here.
43    pub(crate) fn from_device_architecture(name: &str) -> Result<Self> {
44        Self::new(name.split(':').next().unwrap_or(name))
45    }
46    /// Exact architecture key.
47    pub fn as_str(&self) -> &str {
48        self.0.to_str().expect("validated ASCII target")
49    }
50    pub(crate) fn as_c_str(&self) -> &CStr {
51        &self.0
52    }
53    /// Bundle platform and architecture identifier.
54    pub fn manifest_key(&self) -> String {
55        format!("x86_64-unknown-linux-gnu-{}", self.as_str())
56    }
57}
58
59impl Default for Target {
60    fn default() -> Self {
61        Self::new(TARGET_KEY).expect("valid default target")
62    }
63}
64
65#[cfg(test)]
66mod tests {
67    use super::*;
68    #[test]
69    fn device_features_are_separate_from_target_keys() {
70        for name in ["gfx90a", "gfx90a:sramecc+:xnack-", "gfx90a:xnack+"] {
71            assert_eq!(
72                Target::from_device_architecture(name).unwrap().as_str(),
73                "gfx90a"
74            );
75        }
76        assert!(Target::from_device_architecture(":xnack-").is_err());
77        assert!(Target::from_device_architecture("invalid:xnack-").is_err());
78        assert!(
79            Target::new("gfx90a:sramecc+:xnack-")
80                .unwrap_err()
81                .to_string()
82                .contains("bare AMDGPU target")
83        );
84    }
85}