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 pub fn file_extension(&self) -> String {
474 match self.out {
475 NagaOut::WGSL => "wgsl".to_string(),
476 }
477 }
478}
479
480impl SpirvTargetVariant for NagaTarget {
481 fn validate(&self) -> Result<(), TargetError> {
482 Ok(())
483 }
484
485 fn to_spirv_tools(&self) -> spirv_tools::TargetEnv {
486 Self::EMIT_SPIRV_LIKE.to_spirv_tools()
487 }
488
489 fn spirv_version(&self) -> SpirvVersion {
490 Self::EMIT_SPIRV_LIKE.spirv_version()
491 }
492}
493
494impl FromStr for NagaTarget {
495 type Err = TargetError;
496
497 fn from_str(s: &str) -> Result<Self, Self::Err> {
498 let s = s
499 .strip_prefix("naga-")
500 .ok_or_else(|| TargetError::UnknownTarget(s.to_owned()))?;
501 Ok(Self::new(FromStr::from_str(s).map_err(|_e| {
502 TargetError::InvalidNagaVariant(s.to_owned())
503 })?))
504 }
505}
506
507impl Display for NagaTarget {
508 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
509 write!(f, "naga-{}", self.out)
510 }
511}
512
513#[derive(Copy, Clone, Debug, Eq, PartialEq)]
515#[non_exhaustive]
516pub enum SpirvTarget {
517 Universal(UniversalTarget),
518 Vulkan(VulkanTarget),
519 OpenGL(OpenGLTarget),
520 Naga(NagaTarget),
521}
522
523impl SpirvTarget {
524 pub const UNIVERSAL_1_0: Self = Self::Universal(UniversalTarget::UNIVERSAL_1_0);
525 pub const UNIVERSAL_1_1: Self = Self::Universal(UniversalTarget::UNIVERSAL_1_1);
526 pub const UNIVERSAL_1_2: Self = Self::Universal(UniversalTarget::UNIVERSAL_1_2);
527 pub const UNIVERSAL_1_3: Self = Self::Universal(UniversalTarget::UNIVERSAL_1_3);
528 pub const UNIVERSAL_1_4: Self = Self::Universal(UniversalTarget::UNIVERSAL_1_4);
529 pub const UNIVERSAL_1_5: Self = Self::Universal(UniversalTarget::UNIVERSAL_1_5);
530 pub const UNIVERSAL_1_6: Self = Self::Universal(UniversalTarget::UNIVERSAL_1_6);
531 pub const VULKAN_1_0: Self = Self::Vulkan(VulkanTarget::VULKAN_1_0);
532 pub const VULKAN_1_1: Self = Self::Vulkan(VulkanTarget::VULKAN_1_1);
533 pub const VULKAN_1_1_SPV_1_4: Self = Self::Vulkan(VulkanTarget::VULKAN_1_1_SPV_1_4);
534 pub const VULKAN_1_2: Self = Self::Vulkan(VulkanTarget::VULKAN_1_2);
535 pub const VULKAN_1_3: Self = Self::Vulkan(VulkanTarget::VULKAN_1_3);
536 pub const VULKAN_1_4: Self = Self::Vulkan(VulkanTarget::VULKAN_1_4);
537 pub const OPENGL_4_0: Self = Self::OpenGL(OpenGLTarget::OPENGL_4_0);
538 pub const OPENGL_4_1: Self = Self::OpenGL(OpenGLTarget::OPENGL_4_1);
539 pub const OPENGL_4_2: Self = Self::OpenGL(OpenGLTarget::OPENGL_4_2);
540 pub const OPENGL_4_3: Self = Self::OpenGL(OpenGLTarget::OPENGL_4_3);
541 pub const OPENGL_4_5: Self = Self::OpenGL(OpenGLTarget::OPENGL_4_5);
542 pub const NAGA_WGSL: Self = Self::Naga(NagaTarget::NAGA_WGSL);
543
544 #[allow(clippy::match_same_arms)]
545 pub const fn memory_model(&self) -> MemoryModel {
546 match self {
547 SpirvTarget::Universal(_) => MemoryModel::Simple,
548 SpirvTarget::Vulkan(_) => MemoryModel::Vulkan,
549 SpirvTarget::OpenGL(_) => MemoryModel::GLSL450,
550 SpirvTarget::Naga(_) => MemoryModel::Vulkan,
551 }
552 }
553}
554
555impl SpirvTargetVariant for SpirvTarget {
556 fn validate(&self) -> Result<(), TargetError> {
557 match self {
558 SpirvTarget::Universal(t) => t.validate(),
559 SpirvTarget::Vulkan(t) => t.validate(),
560 SpirvTarget::OpenGL(t) => t.validate(),
561 SpirvTarget::Naga(t) => t.validate(),
562 }
563 }
564
565 fn to_spirv_tools(&self) -> spirv_tools::TargetEnv {
566 match self {
567 SpirvTarget::Universal(t) => t.to_spirv_tools(),
568 SpirvTarget::Vulkan(t) => t.to_spirv_tools(),
569 SpirvTarget::OpenGL(t) => t.to_spirv_tools(),
570 SpirvTarget::Naga(t) => t.to_spirv_tools(),
571 }
572 }
573
574 fn spirv_version(&self) -> SpirvVersion {
575 match self {
576 SpirvTarget::Universal(t) => t.spirv_version(),
577 SpirvTarget::Vulkan(t) => t.spirv_version(),
578 SpirvTarget::OpenGL(t) => t.spirv_version(),
579 SpirvTarget::Naga(t) => t.spirv_version(),
580 }
581 }
582}
583
584impl SpirvTarget {
585 pub fn parse_env(s: &str) -> Result<Self, TargetError> {
586 let mut result;
587 result = UniversalTarget::from_str(s).map(Self::Universal);
588 if matches!(result, Err(TargetError::UnknownTarget(..))) {
589 result = VulkanTarget::from_str(s).map(Self::Vulkan);
590 }
591 if matches!(result, Err(TargetError::UnknownTarget(..))) {
592 result = OpenGLTarget::from_str(s).map(Self::OpenGL);
593 }
594 if matches!(result, Err(TargetError::UnknownTarget(..))) {
595 result = NagaTarget::from_str(s).map(Self::Naga);
596 }
597 result
598 }
599
600 pub fn parse_target(s: &str) -> Result<Self, TargetError> {
601 let s = s
602 .strip_prefix(SPIRV_TARGET_PREFIX)
603 .ok_or_else(|| TargetError::UnknownTarget(s.to_owned()))?;
604 Self::parse_env(s)
605 }
606
607 pub fn parse(s: &str) -> Result<Self, TargetError> {
608 Self::parse_env(s.strip_prefix(SPIRV_TARGET_PREFIX).unwrap_or(s))
609 }
610
611 pub fn env(&self) -> String {
613 match self {
614 SpirvTarget::Universal(t) => t.to_string(),
615 SpirvTarget::Vulkan(t) => t.to_string(),
616 SpirvTarget::OpenGL(t) => t.to_string(),
617 SpirvTarget::Naga(t) => t.to_string(),
618 }
619 }
620
621 pub fn target(&self) -> String {
623 format!("{}{}", SPIRV_TARGET_PREFIX, self.env())
624 }
625
626 pub fn file_extension(&self) -> String {
627 match self {
628 SpirvTarget::Universal(_) | SpirvTarget::Vulkan(_) | SpirvTarget::OpenGL(_) => {
629 "spv".to_string()
630 }
631 SpirvTarget::Naga(t) => t.file_extension(),
632 }
633 }
634
635 pub fn all_targets() -> impl Iterator<Item = Self> {
636 UniversalTarget::ALL_UNIVERSAL_TARGETS
637 .iter()
638 .map(|t| Self::Universal(*t))
639 .chain(
640 VulkanTarget::ALL_VULKAN_TARGETS
641 .iter()
642 .map(|t| Self::Vulkan(*t)),
643 )
644 .chain(
645 OpenGLTarget::ALL_OPENGL_TARGETS
646 .iter()
647 .map(|t| Self::OpenGL(*t)),
648 )
649 .chain(NagaTarget::ALL_NAGA_TARGETS.iter().map(|t| Self::Naga(*t)))
650 }
651}
652
653impl SpirvTarget {
654 pub fn rustc_target(&self) -> Target {
655 let mut o = TargetOptions::default();
656 o.simd_types_indirect = false;
657 o.crt_static_allows_dylibs = true;
658 o.crt_static_respected = true;
659 o.dll_prefix = "".into();
660 o.dll_suffix = ".spv.json".into();
661 o.dynamic_linking = true;
662 o.emit_debug_gdb_scripts = false;
663 o.linker_flavor = LinkerFlavor::Unix(Cc::No);
664 o.panic_strategy = PanicStrategy::Abort;
665 o.env = Env::Other(self.env().into());
666 o.is_like_gpu = true;
668 o.main_needs_argc_argv = false;
670
671 Target {
672 llvm_target: self.target().into(),
673 metadata: Default::default(),
674 pointer_width: 32,
675 data_layout: "e-m:e-p:32:32:32-i64:64-i128:128-n8:16:32:64".into(),
676 arch: Arch::Other(SPIRV_ARCH.into()),
677 options: o,
678 }
679 }
680}
681
682#[cfg(test)]
683mod tests {
684 use super::*;
685
686 #[test]
687 fn test_prefix_matches_arch_vendor() {
688 assert_eq!(SPIRV_TARGET_PREFIX, format!("{SPIRV_ARCH}-unknown-"));
689 }
690
691 #[test]
692 fn test_str_roundtrip_target() {
693 for target in SpirvTarget::all_targets() {
694 assert_eq!(SpirvTarget::parse_target(&target.target()), Ok(target));
695 }
696 }
697
698 #[test]
699 fn test_str_roundtrip_env() {
700 for target in SpirvTarget::all_targets() {
701 assert_eq!(SpirvTarget::parse_env(&target.env()), Ok(target));
702 }
703 }
704
705 #[test]
706 fn test_unknown_target() {
707 let result = SpirvTarget::parse_env("unknown");
708 assert!(
709 matches!(result, Err(TargetError::UnknownTarget(..))),
710 "{result:?}",
711 );
712 let result = SpirvTarget::parse_env("vulkan6.8");
713 assert!(
714 matches!(result, Err(TargetError::InvalidTargetVersion(..))),
715 "{result:?}",
716 );
717 let result = SpirvTarget::parse_env("vulkan1.4-spv1.0");
718 assert!(
719 matches!(result, Err(TargetError::InvalidTargetVersion(..))),
720 "{result:?}",
721 );
722 let result = SpirvTarget::parse_env("spv1.4-spv1.0");
723 assert!(
724 matches!(result, Err(TargetError::UnknownTarget(..))),
725 "{result:?}",
726 );
727 }
728}