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 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#[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#[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
172pub trait SpirvTargetVariant {
174 fn validate(&self) -> Result<(), TargetError>;
176 fn to_spirv_tools(&self) -> spirv_tools::TargetEnv;
178 fn spirv_version(&self) -> SpirvVersion;
180}
181
182#[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#[derive(Copy, Clone, Debug, Eq, PartialEq)]
271pub struct VulkanTarget {
272 pub version: Version,
273 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#[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 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#[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 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#[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 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 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 o.is_like_gpu = true;
654 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}