Skip to main content

rustc_codegen_spirv/
target.rs

1use rspirv::spirv::MemoryModel;
2use rustc_codegen_spirv_types::{SPIRV_ARCH, SPIRV_TARGET_PREFIX};
3use rustc_target::spec::{Arch, Cc, Env, LinkerFlavor, PanicStrategy, Target, TargetOptions};
4use std::cmp::Ordering;
5use std::fmt::{Debug, Display, Formatter};
6use std::ops::{Deref, DerefMut};
7use std::str::FromStr;
8use strum::{Display, EnumString, IntoStaticStr};
9
10#[derive(Clone, Eq, PartialEq)]
11pub enum TargetError {
12    /// If during parsing a target variant returns `UnknownTarget`, further variants will attempt to parse the string.
13    /// Returning another error means that you have recognized the target but something else is invalid, and we should
14    /// abort the parsing with your error.
15    UnknownTarget(String),
16    InvalidTargetVersion(SpirvTarget),
17    InvalidNagaVariant(String),
18}
19
20impl Display for TargetError {
21    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
22        match self {
23            TargetError::UnknownTarget(target) => {
24                write!(f, "Unknown target `{target}`")
25            }
26            TargetError::InvalidTargetVersion(target) => {
27                write!(f, "Invalid version in target `{}`", target.env())
28            }
29            TargetError::InvalidNagaVariant(target) => {
30                write!(f, "Unknown naga out variant `{target}`")
31            }
32        }
33    }
34}
35
36impl Debug for TargetError {
37    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
38        Display::fmt(self, f)
39    }
40}
41
42/// A version with a major and minor component
43#[derive(Copy, Clone, Debug, Eq, PartialEq)]
44pub struct Version(pub u8, pub u8);
45
46impl Version {
47    pub const fn new(major: u8, minor: u8) -> Self {
48        Self(major, minor)
49    }
50
51    pub const fn from_tuple(tuple: (u8, u8)) -> Self {
52        Self(tuple.0, tuple.1)
53    }
54
55    pub const fn to_tuple(self) -> (u8, u8) {
56        (self.0, self.1)
57    }
58
59    fn parse_unbounded(s: &str) -> Option<(Self, &str)> {
60        fn parse_num(s: &str) -> Option<(&str, u8)> {
61            let mut value = 0;
62            let mut len = 0;
63            for digit in s.as_bytes().iter().copied() {
64                if !digit.is_ascii_digit() {
65                    break;
66                }
67                if value == 0 && len > 0 {
68                    return None;
69                }
70                value = value * 10 + (digit - b'0') as u64;
71                len += 1;
72            }
73            (len > 0).then_some((&s[len..], u8::try_from(value).ok()?))
74        }
75        let (s, major) = parse_num(s)?;
76        if !matches!(s.chars().next(), Some('.')) {
77            return None;
78        }
79        let s = &s[1..];
80        let (s, minor) = parse_num(s)?;
81        Some((Self(major, minor), s))
82    }
83}
84
85impl FromStr for Version {
86    type Err = ();
87
88    fn from_str(s: &str) -> Result<Self, Self::Err> {
89        let (out, s) = Self::parse_unbounded(s).ok_or(())?;
90        s.is_empty().then_some(out).ok_or(())
91    }
92}
93
94#[inline]
95fn from_str_to_parse_unbounded<T: SpirvTargetVariant>(
96    s: &str,
97    parse_unbounded: impl FnOnce(&str) -> Option<(T, &str)>,
98) -> Result<T, TargetError> {
99    let unknown = || TargetError::UnknownTarget(s.to_owned());
100    let (out, s) = parse_unbounded(s).ok_or_else(unknown)?;
101    if !s.is_empty() {
102        return Err(unknown());
103    }
104    out.validate()?;
105    Ok(out)
106}
107
108impl Display for Version {
109    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
110        write!(f, "{}.{}", self.0, self.1)
111    }
112}
113
114impl PartialOrd<Self> for Version {
115    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
116        Some(self.cmp(other))
117    }
118}
119
120impl Ord for Version {
121    fn cmp(&self, other: &Self) -> Ordering {
122        self.0.cmp(&other.0).then(self.1.cmp(&other.1))
123    }
124}
125
126/// A SPIR-V version
127///
128/// For the SPIR-V universal target, see [`UniversalTarget`]
129#[repr(transparent)]
130#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
131pub struct SpirvVersion {
132    pub version: Version,
133}
134
135impl SpirvVersion {
136    pub const V1_0: Self = Self::new(1, 0);
137    pub const V1_1: Self = Self::new(1, 1);
138    pub const V1_2: Self = Self::new(1, 2);
139    pub const V1_3: Self = Self::new(1, 3);
140    pub const V1_4: Self = Self::new(1, 4);
141    pub const V1_5: Self = Self::new(1, 5);
142    pub const V1_6: Self = Self::new(1, 6);
143
144    #[inline]
145    pub const fn new(major: u8, minor: u8) -> Self {
146        Self {
147            version: Version::new(major, minor),
148        }
149    }
150}
151
152impl From<Version> for SpirvVersion {
153    fn from(version: Version) -> Self {
154        Self { version }
155    }
156}
157
158impl Deref for SpirvVersion {
159    type Target = Version;
160
161    fn deref(&self) -> &Self::Target {
162        &self.version
163    }
164}
165
166impl DerefMut for SpirvVersion {
167    fn deref_mut(&mut self) -> &mut Self::Target {
168        &mut self.version
169    }
170}
171
172/// A trait to describe common properties between different target variants
173pub trait SpirvTargetVariant {
174    /// Validate the target version
175    fn validate(&self) -> Result<(), TargetError>;
176    /// Get the [`spirv_tools::TargetEnv`] to use for `spirv-val` and `spirv-opt`. May panic if version is invalid.
177    fn to_spirv_tools(&self) -> spirv_tools::TargetEnv;
178    /// Get the [`SpirvVersion`] of this target. May panic if version is invalid.
179    fn spirv_version(&self) -> SpirvVersion;
180}
181
182/// A SPIR-V universal target
183///
184/// This is different from [`SpirvVersion`] to prevent misuse! [`Self::spirv_version`] doesn't return a
185/// [`UniversalTarget`] but a [`SpirvVersion`], which can't accidentally be passed onwards as a target. So you can't
186/// accidentally turn a [`VulkanTarget`] target into a [`UniversalTarget`].
187#[derive(Copy, Clone, Debug, Eq, PartialEq)]
188pub struct UniversalTarget {
189    pub version: Version,
190}
191
192impl UniversalTarget {
193    pub const UNIVERSAL_1_0: Self = Self::new(Version(1, 0));
194    pub const UNIVERSAL_1_1: Self = Self::new(Version(1, 1));
195    pub const UNIVERSAL_1_2: Self = Self::new(Version(1, 2));
196    pub const UNIVERSAL_1_3: Self = Self::new(Version(1, 3));
197    pub const UNIVERSAL_1_4: Self = Self::new(Version(1, 4));
198    pub const UNIVERSAL_1_5: Self = Self::new(Version(1, 5));
199    pub const UNIVERSAL_1_6: Self = Self::new(Version(1, 6));
200    pub const ALL_UNIVERSAL_TARGETS: &'static [Self] = &[
201        Self::UNIVERSAL_1_0,
202        Self::UNIVERSAL_1_1,
203        Self::UNIVERSAL_1_2,
204        Self::UNIVERSAL_1_3,
205        Self::UNIVERSAL_1_4,
206        Self::UNIVERSAL_1_5,
207        Self::UNIVERSAL_1_6,
208    ];
209
210    pub const fn new(version: Version) -> Self {
211        Self { version }
212    }
213
214    pub const fn properties(self) -> Result<spirv_tools::TargetEnv, TargetError> {
215        Ok(match self.version {
216            Version(1, 0) => spirv_tools::TargetEnv::Universal_1_0,
217            Version(1, 1) => spirv_tools::TargetEnv::Universal_1_1,
218            Version(1, 2) => spirv_tools::TargetEnv::Universal_1_2,
219            Version(1, 3) => spirv_tools::TargetEnv::Universal_1_3,
220            Version(1, 4) => spirv_tools::TargetEnv::Universal_1_4,
221            Version(1, 5) => spirv_tools::TargetEnv::Universal_1_5,
222            Version(1, 6) => spirv_tools::TargetEnv::Universal_1_6,
223            _ => {
224                return Err(TargetError::InvalidTargetVersion(SpirvTarget::Universal(
225                    self,
226                )));
227            }
228        })
229    }
230}
231
232impl SpirvTargetVariant for UniversalTarget {
233    fn validate(&self) -> Result<(), TargetError> {
234        self.properties()?;
235        Ok(())
236    }
237
238    fn to_spirv_tools(&self) -> spirv_tools::TargetEnv {
239        self.properties().unwrap()
240    }
241
242    fn spirv_version(&self) -> SpirvVersion {
243        SpirvVersion::from(self.version)
244    }
245}
246
247impl UniversalTarget {
248    fn parse_unbounded(s: &str) -> Option<(Self, &str)> {
249        let s = s.strip_prefix("spv")?;
250        let (version, s) = Version::parse_unbounded(s)?;
251        Some((Self::new(version), s))
252    }
253}
254
255impl FromStr for UniversalTarget {
256    type Err = TargetError;
257
258    fn from_str(s: &str) -> Result<Self, Self::Err> {
259        from_str_to_parse_unbounded(s, Self::parse_unbounded)
260    }
261}
262
263impl Display for UniversalTarget {
264    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
265        write!(f, "spv{}", self.version)
266    }
267}
268
269/// A Vulkan target
270#[derive(Copy, Clone, Debug, Eq, PartialEq)]
271pub struct VulkanTarget {
272    pub version: Version,
273    /// optional, may specify a spv version
274    pub spv_version: Option<Version>,
275}
276
277impl VulkanTarget {
278    pub const VULKAN_1_0: Self = Self::new(Version(1, 0));
279    pub const VULKAN_1_1: Self = Self::new(Version(1, 1));
280    pub const VULKAN_1_1_SPV_1_4: Self = Self {
281        version: Version(1, 1),
282        spv_version: Some(Version(1, 4)),
283    };
284    pub const VULKAN_1_2: Self = Self::new(Version(1, 2));
285    pub const VULKAN_1_3: Self = Self::new(Version(1, 3));
286    pub const VULKAN_1_4: Self = Self::new(Version(1, 4));
287    pub const ALL_VULKAN_TARGETS: &'static [Self] = &[
288        Self::VULKAN_1_0,
289        Self::VULKAN_1_1,
290        Self::VULKAN_1_1_SPV_1_4,
291        Self::VULKAN_1_2,
292        Self::VULKAN_1_3,
293        Self::VULKAN_1_4,
294    ];
295
296    pub const fn new(version: Version) -> Self {
297        Self {
298            version,
299            spv_version: None,
300        }
301    }
302
303    pub const fn properties(self) -> Result<(SpirvVersion, spirv_tools::TargetEnv), TargetError> {
304        let err = Err(TargetError::InvalidTargetVersion(SpirvTarget::Vulkan(self)));
305        Ok(match (self.version, self.spv_version) {
306            (Version(1, 0), None) => (SpirvVersion::new(1, 0), spirv_tools::TargetEnv::Vulkan_1_0),
307            (Version(1, 1), None) => (SpirvVersion::new(1, 3), spirv_tools::TargetEnv::Vulkan_1_1),
308            (Version(1, 1), Some(Version(1, 4))) => (
309                SpirvVersion::new(1, 4),
310                spirv_tools::TargetEnv::Vulkan_1_1_Spirv_1_4,
311            ),
312            (Version(1, 2), None) => (SpirvVersion::new(1, 5), spirv_tools::TargetEnv::Vulkan_1_2),
313            (Version(1, 3), None) => (SpirvVersion::new(1, 6), spirv_tools::TargetEnv::Vulkan_1_3),
314            (Version(1, 4), None) => (SpirvVersion::new(1, 6), spirv_tools::TargetEnv::Vulkan_1_4),
315            _ => return err,
316        })
317    }
318}
319
320impl SpirvTargetVariant for VulkanTarget {
321    fn validate(&self) -> Result<(), TargetError> {
322        self.properties()?;
323        Ok(())
324    }
325
326    fn to_spirv_tools(&self) -> spirv_tools::TargetEnv {
327        self.properties().unwrap().1
328    }
329
330    fn spirv_version(&self) -> SpirvVersion {
331        self.properties().unwrap().0
332    }
333}
334
335impl VulkanTarget {
336    fn parse_unbounded(s: &str) -> Option<(Self, &str)> {
337        let s = s.strip_prefix("vulkan")?;
338        let (version, s) = Version::parse_unbounded(s)?;
339        let (spv_version, s) = if !s.is_empty() {
340            let s = s.strip_prefix("-spv")?;
341            let (spv_version, s) = Version::parse_unbounded(s)?;
342            (Some(spv_version), s)
343        } else {
344            (None, s)
345        };
346        Some((
347            Self {
348                version,
349                spv_version,
350            },
351            s,
352        ))
353    }
354}
355
356impl FromStr for VulkanTarget {
357    type Err = TargetError;
358
359    fn from_str(s: &str) -> Result<Self, Self::Err> {
360        from_str_to_parse_unbounded(s, Self::parse_unbounded)
361    }
362}
363
364impl Display for VulkanTarget {
365    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
366        write!(f, "vulkan{}", self.version)?;
367        if let Some(spv_version) = self.spv_version {
368            write!(f, "-spv{spv_version}")?;
369        }
370        Ok(())
371    }
372}
373
374/// An OpenGL target
375#[derive(Copy, Clone, Debug, Eq, PartialEq)]
376pub struct OpenGLTarget {
377    pub version: Version,
378}
379
380impl OpenGLTarget {
381    pub const OPENGL_4_0: Self = Self::new(Version(4, 0));
382    pub const OPENGL_4_1: Self = Self::new(Version(4, 1));
383    pub const OPENGL_4_2: Self = Self::new(Version(4, 2));
384    pub const OPENGL_4_3: Self = Self::new(Version(4, 3));
385    pub const OPENGL_4_5: Self = Self::new(Version(4, 5));
386    pub const ALL_OPENGL_TARGETS: &'static [Self] = &[
387        Self::OPENGL_4_0,
388        Self::OPENGL_4_1,
389        Self::OPENGL_4_2,
390        Self::OPENGL_4_3,
391        Self::OPENGL_4_5,
392    ];
393
394    pub const fn new(version: Version) -> Self {
395        Self { version }
396    }
397
398    pub const fn properties(self) -> Result<spirv_tools::TargetEnv, TargetError> {
399        Ok(match self.version {
400            Version(4, 0) => spirv_tools::TargetEnv::OpenGL_4_0,
401            Version(4, 1) => spirv_tools::TargetEnv::OpenGL_4_1,
402            Version(4, 2) => spirv_tools::TargetEnv::OpenGL_4_2,
403            Version(4, 3) => spirv_tools::TargetEnv::OpenGL_4_3,
404            Version(4, 5) => spirv_tools::TargetEnv::OpenGL_4_5,
405            _ => {
406                return Err(TargetError::InvalidTargetVersion(SpirvTarget::OpenGL(self)));
407            }
408        })
409    }
410}
411
412impl SpirvTargetVariant for OpenGLTarget {
413    fn validate(&self) -> Result<(), TargetError> {
414        self.properties()?;
415        Ok(())
416    }
417
418    fn to_spirv_tools(&self) -> spirv_tools::TargetEnv {
419        self.properties().unwrap()
420    }
421
422    /// always 1.0
423    fn spirv_version(&self) -> SpirvVersion {
424        SpirvVersion::new(1, 0)
425    }
426}
427
428impl OpenGLTarget {
429    fn parse_unbounded(s: &str) -> Option<(Self, &str)> {
430        let s = s.strip_prefix("opengl")?;
431        let (version, s) = Version::parse_unbounded(s)?;
432        Some((Self { version }, s))
433    }
434}
435
436impl FromStr for OpenGLTarget {
437    type Err = TargetError;
438
439    fn from_str(s: &str) -> Result<Self, Self::Err> {
440        from_str_to_parse_unbounded(s, Self::parse_unbounded)
441    }
442}
443
444impl Display for OpenGLTarget {
445    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
446        write!(f, "opengl{}", self.version)
447    }
448}
449
450/// A naga target
451#[derive(Copy, Clone, Debug, Eq, PartialEq)]
452pub struct NagaTarget {
453    pub out: NagaOut,
454}
455
456#[derive(Copy, Clone, Debug, Eq, PartialEq, IntoStaticStr, Display, EnumString)]
457#[allow(clippy::upper_case_acronyms)]
458pub enum NagaOut {
459    #[strum(to_string = "wgsl")]
460    WGSL,
461}
462
463impl NagaTarget {
464    pub const NAGA_WGSL: Self = NagaTarget::new(NagaOut::WGSL);
465    pub const ALL_NAGA_TARGETS: &'static [Self] = &[Self::NAGA_WGSL];
466    /// emit spirv like naga targets were this target
467    pub const EMIT_SPIRV_LIKE: SpirvTarget = SpirvTarget::VULKAN_1_3;
468
469    pub const fn new(out: NagaOut) -> Self {
470        Self { out }
471    }
472}
473
474impl SpirvTargetVariant for NagaTarget {
475    fn validate(&self) -> Result<(), TargetError> {
476        Ok(())
477    }
478
479    fn to_spirv_tools(&self) -> spirv_tools::TargetEnv {
480        Self::EMIT_SPIRV_LIKE.to_spirv_tools()
481    }
482
483    fn spirv_version(&self) -> SpirvVersion {
484        Self::EMIT_SPIRV_LIKE.spirv_version()
485    }
486}
487
488impl FromStr for NagaTarget {
489    type Err = TargetError;
490
491    fn from_str(s: &str) -> Result<Self, Self::Err> {
492        let s = s
493            .strip_prefix("naga-")
494            .ok_or_else(|| TargetError::UnknownTarget(s.to_owned()))?;
495        Ok(Self::new(FromStr::from_str(s).map_err(|_e| {
496            TargetError::InvalidNagaVariant(s.to_owned())
497        })?))
498    }
499}
500
501impl Display for NagaTarget {
502    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
503        write!(f, "naga-{}", self.out)
504    }
505}
506
507/// A rust-gpu target
508#[derive(Copy, Clone, Debug, Eq, PartialEq)]
509#[non_exhaustive]
510pub enum SpirvTarget {
511    Universal(UniversalTarget),
512    Vulkan(VulkanTarget),
513    OpenGL(OpenGLTarget),
514    Naga(NagaTarget),
515}
516
517impl SpirvTarget {
518    pub const UNIVERSAL_1_0: Self = Self::Universal(UniversalTarget::UNIVERSAL_1_0);
519    pub const UNIVERSAL_1_1: Self = Self::Universal(UniversalTarget::UNIVERSAL_1_1);
520    pub const UNIVERSAL_1_2: Self = Self::Universal(UniversalTarget::UNIVERSAL_1_2);
521    pub const UNIVERSAL_1_3: Self = Self::Universal(UniversalTarget::UNIVERSAL_1_3);
522    pub const UNIVERSAL_1_4: Self = Self::Universal(UniversalTarget::UNIVERSAL_1_4);
523    pub const UNIVERSAL_1_5: Self = Self::Universal(UniversalTarget::UNIVERSAL_1_5);
524    pub const UNIVERSAL_1_6: Self = Self::Universal(UniversalTarget::UNIVERSAL_1_6);
525    pub const VULKAN_1_0: Self = Self::Vulkan(VulkanTarget::VULKAN_1_0);
526    pub const VULKAN_1_1: Self = Self::Vulkan(VulkanTarget::VULKAN_1_1);
527    pub const VULKAN_1_1_SPV_1_4: Self = Self::Vulkan(VulkanTarget::VULKAN_1_1_SPV_1_4);
528    pub const VULKAN_1_2: Self = Self::Vulkan(VulkanTarget::VULKAN_1_2);
529    pub const VULKAN_1_3: Self = Self::Vulkan(VulkanTarget::VULKAN_1_3);
530    pub const VULKAN_1_4: Self = Self::Vulkan(VulkanTarget::VULKAN_1_4);
531    pub const OPENGL_4_0: Self = Self::OpenGL(OpenGLTarget::OPENGL_4_0);
532    pub const OPENGL_4_1: Self = Self::OpenGL(OpenGLTarget::OPENGL_4_1);
533    pub const OPENGL_4_2: Self = Self::OpenGL(OpenGLTarget::OPENGL_4_2);
534    pub const OPENGL_4_3: Self = Self::OpenGL(OpenGLTarget::OPENGL_4_3);
535    pub const OPENGL_4_5: Self = Self::OpenGL(OpenGLTarget::OPENGL_4_5);
536    pub const NAGA_WGSL: Self = Self::Naga(NagaTarget::NAGA_WGSL);
537
538    #[allow(clippy::match_same_arms)]
539    pub const fn memory_model(&self) -> MemoryModel {
540        match self {
541            SpirvTarget::Universal(_) => MemoryModel::Simple,
542            SpirvTarget::Vulkan(_) => MemoryModel::Vulkan,
543            SpirvTarget::OpenGL(_) => MemoryModel::GLSL450,
544            SpirvTarget::Naga(_) => MemoryModel::Vulkan,
545        }
546    }
547}
548
549impl SpirvTargetVariant for SpirvTarget {
550    fn validate(&self) -> Result<(), TargetError> {
551        match self {
552            SpirvTarget::Universal(t) => t.validate(),
553            SpirvTarget::Vulkan(t) => t.validate(),
554            SpirvTarget::OpenGL(t) => t.validate(),
555            SpirvTarget::Naga(t) => t.validate(),
556        }
557    }
558
559    fn to_spirv_tools(&self) -> spirv_tools::TargetEnv {
560        match self {
561            SpirvTarget::Universal(t) => t.to_spirv_tools(),
562            SpirvTarget::Vulkan(t) => t.to_spirv_tools(),
563            SpirvTarget::OpenGL(t) => t.to_spirv_tools(),
564            SpirvTarget::Naga(t) => t.to_spirv_tools(),
565        }
566    }
567
568    fn spirv_version(&self) -> SpirvVersion {
569        match self {
570            SpirvTarget::Universal(t) => t.spirv_version(),
571            SpirvTarget::Vulkan(t) => t.spirv_version(),
572            SpirvTarget::OpenGL(t) => t.spirv_version(),
573            SpirvTarget::Naga(t) => t.spirv_version(),
574        }
575    }
576}
577
578impl SpirvTarget {
579    pub fn parse_env(s: &str) -> Result<Self, TargetError> {
580        let mut result;
581        result = UniversalTarget::from_str(s).map(Self::Universal);
582        if matches!(result, Err(TargetError::UnknownTarget(..))) {
583            result = VulkanTarget::from_str(s).map(Self::Vulkan);
584        }
585        if matches!(result, Err(TargetError::UnknownTarget(..))) {
586            result = OpenGLTarget::from_str(s).map(Self::OpenGL);
587        }
588        if matches!(result, Err(TargetError::UnknownTarget(..))) {
589            result = NagaTarget::from_str(s).map(Self::Naga);
590        }
591        result
592    }
593
594    pub fn parse_target(s: &str) -> Result<Self, TargetError> {
595        let s = s
596            .strip_prefix(SPIRV_TARGET_PREFIX)
597            .ok_or_else(|| TargetError::UnknownTarget(s.to_owned()))?;
598        Self::parse_env(s)
599    }
600
601    pub fn parse(s: &str) -> Result<Self, TargetError> {
602        Self::parse_env(s.strip_prefix(SPIRV_TARGET_PREFIX).unwrap_or(s))
603    }
604
605    /// returns the target env, e.g. `vulkan1.3`
606    pub fn env(&self) -> String {
607        match self {
608            SpirvTarget::Universal(t) => t.to_string(),
609            SpirvTarget::Vulkan(t) => t.to_string(),
610            SpirvTarget::OpenGL(t) => t.to_string(),
611            SpirvTarget::Naga(t) => t.to_string(),
612        }
613    }
614
615    /// returns the full target, e.g. `spirv-unknown-vulkan1.3`
616    pub fn target(&self) -> String {
617        format!("{}{}", SPIRV_TARGET_PREFIX, self.env())
618    }
619
620    pub fn all_targets() -> impl Iterator<Item = Self> {
621        UniversalTarget::ALL_UNIVERSAL_TARGETS
622            .iter()
623            .map(|t| Self::Universal(*t))
624            .chain(
625                VulkanTarget::ALL_VULKAN_TARGETS
626                    .iter()
627                    .map(|t| Self::Vulkan(*t)),
628            )
629            .chain(
630                OpenGLTarget::ALL_OPENGL_TARGETS
631                    .iter()
632                    .map(|t| Self::OpenGL(*t)),
633            )
634            .chain(NagaTarget::ALL_NAGA_TARGETS.iter().map(|t| Self::Naga(*t)))
635    }
636}
637
638impl SpirvTarget {
639    pub fn rustc_target(&self) -> Target {
640        let mut o = TargetOptions::default();
641        o.simd_types_indirect = false;
642        o.allows_weak_linkage = false;
643        o.crt_static_allows_dylibs = true;
644        o.crt_static_respected = true;
645        o.dll_prefix = "".into();
646        o.dll_suffix = ".spv.json".into();
647        o.dynamic_linking = true;
648        o.emit_debug_gdb_scripts = false;
649        o.linker_flavor = LinkerFlavor::Unix(Cc::No);
650        o.panic_strategy = PanicStrategy::Abort;
651        o.env = Env::Other(self.env().into());
652        // Note(@firestar99): not sure if this does anything
653        o.is_like_gpu = true;
654        // TODO: Investigate if main_needs_argc_argv is useful (for building exes)
655        o.main_needs_argc_argv = false;
656
657        Target {
658            llvm_target: self.target().into(),
659            metadata: Default::default(),
660            pointer_width: 32,
661            data_layout: "e-m:e-p:32:32:32-i64:64-n8:16:32:64".into(),
662            arch: Arch::Other(SPIRV_ARCH.into()),
663            options: o,
664        }
665    }
666}
667
668#[cfg(test)]
669mod tests {
670    use super::*;
671
672    #[test]
673    fn test_prefix_matches_arch_vendor() {
674        assert_eq!(SPIRV_TARGET_PREFIX, format!("{SPIRV_ARCH}-unknown-"));
675    }
676
677    #[test]
678    fn test_str_roundtrip_target() {
679        for target in SpirvTarget::all_targets() {
680            assert_eq!(SpirvTarget::parse_target(&target.target()), Ok(target));
681        }
682    }
683
684    #[test]
685    fn test_str_roundtrip_env() {
686        for target in SpirvTarget::all_targets() {
687            assert_eq!(SpirvTarget::parse_env(&target.env()), Ok(target));
688        }
689    }
690
691    #[test]
692    fn test_unknown_target() {
693        let result = SpirvTarget::parse_env("unknown");
694        assert!(
695            matches!(result, Err(TargetError::UnknownTarget(..))),
696            "{result:?}",
697        );
698        let result = SpirvTarget::parse_env("vulkan6.8");
699        assert!(
700            matches!(result, Err(TargetError::InvalidTargetVersion(..))),
701            "{result:?}",
702        );
703        let result = SpirvTarget::parse_env("vulkan1.4-spv1.0");
704        assert!(
705            matches!(result, Err(TargetError::InvalidTargetVersion(..))),
706            "{result:?}",
707        );
708        let result = SpirvTarget::parse_env("spv1.4-spv1.0");
709        assert!(
710            matches!(result, Err(TargetError::UnknownTarget(..))),
711            "{result:?}",
712        );
713    }
714}