Skip to content

Commit 775b5d5

Browse files
eddybLegNeato
authored andcommitted
spirv-builder: pass JSON files to --target instead of relying on the backend's target_override.
1 parent 40456ae commit 775b5d5

25 files changed

+534
-74
lines changed

Cargo.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/rustc_codegen_spirv-types/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@ repository.workspace = true
1010
[dependencies]
1111
rspirv = "0.11"
1212
serde = { version = "1.0", features = ["derive"] }
13+
serde_json = "1.0"

crates/rustc_codegen_spirv-types/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,7 @@ pub use rspirv::spirv::Capability;
44

55
mod compile_result;
66
pub use compile_result::*;
7+
8+
// HACK(eddyb) allows downstream crates to access the correct version directly.
9+
pub use serde;
10+
pub use serde_json;

crates/rustc_codegen_spirv/Cargo.toml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,6 @@ rspirv = "0.11"
5454
rustc_codegen_spirv-types.workspace = true
5555
rustc-demangle = "0.1.21"
5656
sanitize-filename = "0.4"
57-
serde = { version = "1.0", features = ["derive"] }
58-
serde_json = "1.0"
5957
smallvec = { version = "1.6.1", features = ["union"] }
6058
spirt = "0.3.0"
6159
spirv-tools.workspace = true

crates/rustc_codegen_spirv/src/codegen_cx/mod.rs

Lines changed: 69 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use crate::spirv_type::{SpirvType, SpirvTypePrinter, TypeCache};
1010
use crate::symbols::Symbols;
1111
use crate::target::SpirvTarget;
1212

13+
use itertools::Itertools as _;
1314
use rspirv::dr::{Module, Operand};
1415
use rspirv::spirv::{Decoration, LinkageType, Op, Word};
1516
use rustc_ast::ast::{InlineAsmOptions, InlineAsmTemplatePiece};
@@ -27,7 +28,7 @@ use rustc_span::symbol::Symbol;
2728
use rustc_span::{SourceFile, Span, DUMMY_SP};
2829
use rustc_target::abi::call::{FnAbi, PassMode};
2930
use rustc_target::abi::{AddressSpace, HasDataLayout, TargetDataLayout};
30-
use rustc_target::spec::{HasTargetSpec, Target};
31+
use rustc_target::spec::{HasTargetSpec, Target, TargetTriple};
3132
use std::cell::RefCell;
3233
use std::collections::BTreeSet;
3334
use std::iter::once;
@@ -87,6 +88,73 @@ pub struct CodegenCx<'tcx> {
8788

8889
impl<'tcx> CodegenCx<'tcx> {
8990
pub fn new(tcx: TyCtxt<'tcx>, codegen_unit: &'tcx CodegenUnit<'tcx>) -> Self {
91+
// Validate the target spec, as the backend doesn't control `--target`.
92+
let target_triple = tcx.sess.opts.target_triple.triple();
93+
let target: SpirvTarget = target_triple.parse().unwrap_or_else(|_| {
94+
let qualifier = if !target_triple.starts_with("spirv-") {
95+
"non-SPIR-V "
96+
} else {
97+
""
98+
};
99+
tcx.dcx().fatal(format!(
100+
"{qualifier}target `{target_triple}` not supported by `rustc_codegen_spirv`",
101+
))
102+
});
103+
let target_spec_mismatched_jsons = {
104+
use rustc_target::json::ToJson;
105+
106+
// HACK(eddyb) this loads the same `serde_json` used by `rustc_target`.
107+
extern crate serde_json;
108+
109+
let expected = &target.rustc_target();
110+
let found = &tcx.sess.target;
111+
match &tcx.sess.opts.target_triple {
112+
// HACK(eddyb) if `--target=path/to/target/spec.json` was used,
113+
// `tcx.sess.target.to_json()` could still differ from it, and
114+
// ideally `spirv-builder` can be forced to pass an exact match.
115+
//
116+
// FIXME(eddyb) consider the `RUST_TARGET_PATH` env var alternative.
117+
TargetTriple::TargetTriple(_) => {
118+
// FIXME(eddyb) this case should be impossible as upstream
119+
// `rustc` doesn't support `spirv-*` targets!
120+
(expected != found).then(|| [expected, found].map(|spec| spec.to_json()))
121+
}
122+
TargetTriple::TargetJson { contents, .. } => {
123+
let expected = expected.to_json();
124+
let found = serde_json::from_str(contents).unwrap();
125+
(expected != found).then_some([expected, found])
126+
}
127+
}
128+
};
129+
if let Some([expected, found]) = target_spec_mismatched_jsons {
130+
let diff_keys = [&expected, &found]
131+
.into_iter()
132+
.flat_map(|json| json.as_object().into_iter().flat_map(|obj| obj.keys()))
133+
.unique()
134+
.filter(|k| expected.get(k) != found.get(k));
135+
136+
tcx.dcx()
137+
.struct_fatal(format!("mismatched `{target_triple}` target spec"))
138+
.with_note(format!(
139+
"expected (built into `rustc_codegen_spirv`):\n{expected:#}"
140+
))
141+
.with_note(match &tcx.sess.opts.target_triple {
142+
TargetTriple::TargetJson {
143+
path_for_rustdoc,
144+
contents,
145+
..
146+
} if !path_for_rustdoc.as_os_str().is_empty() => {
147+
format!("found (`{}`):\n{contents}", path_for_rustdoc.display())
148+
}
149+
_ => format!("found:\n{found:#}"),
150+
})
151+
.with_help(format!(
152+
"mismatched properties: {}",
153+
diff_keys.map(|k| format!("{k:?}")).join(", ")
154+
))
155+
.emit();
156+
}
157+
90158
let sym = Symbols::get();
91159

92160
let mut feature_names = tcx
@@ -111,7 +179,6 @@ impl<'tcx> CodegenCx<'tcx> {
111179
});
112180

113181
let codegen_args = CodegenArgs::from_session(tcx.sess);
114-
let target = tcx.sess.target.llvm_target.parse().unwrap();
115182

116183
Self {
117184
tcx,

crates/rustc_codegen_spirv/src/lib.rs

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,6 @@ use rustc_middle::ty::{self, Instance, InstanceDef, TyCtxt};
108108
use rustc_session::config::{self, OutputFilenames, OutputType};
109109
use rustc_session::Session;
110110
use rustc_span::symbol::{sym, Symbol};
111-
use rustc_target::spec::{Target, TargetTriple};
112111
use std::any::Any;
113112
use std::fs::{create_dir_all, File};
114113
use std::io::Cursor;
@@ -207,16 +206,6 @@ impl CodegenBackend for SpirvCodegenBackend {
207206
.collect()
208207
}
209208

210-
fn target_override(&self, opts: &config::Options) -> Option<Target> {
211-
match opts.target_triple {
212-
TargetTriple::TargetTriple(ref target) => target
213-
.parse::<target::SpirvTarget>()
214-
.map(|target| target.rustc_target())
215-
.ok(),
216-
TargetTriple::TargetJson { .. } => None,
217-
}
218-
}
219-
220209
fn provide(&self, providers: &mut rustc_middle::util::Providers) {
221210
// FIXME(eddyb) this is currently only passed back to us, specifically
222211
// into `target_machine_factory` (which is a noop), but it might make

crates/rustc_codegen_spirv/src/link.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,9 @@ fn link_exe(
218218
};
219219

220220
let file = File::create(out_filename).unwrap();
221-
serde_json::to_writer(BufWriter::new(file), &compile_result).unwrap();
221+
// FIXME(eddyb) move this functionality into `rustc_codegen_spirv_types`.
222+
rustc_codegen_spirv_types::serde_json::to_writer(BufWriter::new(file), &compile_result)
223+
.unwrap();
222224
}
223225

224226
fn entry_points(module: &rspirv::dr::Module) -> Vec<String> {

crates/spirv-builder/src/lib.rs

Lines changed: 76 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,8 @@ pub use rustc_codegen_spirv_types::{CompileResult, ModuleResult};
118118
#[derive(Debug)]
119119
#[non_exhaustive]
120120
pub enum SpirvBuilderError {
121+
NonSpirvTarget { target: String },
122+
UnsupportedSpirvTargetEnv { target_env: String },
121123
CratePathDoesntExist(PathBuf),
122124
BuildFailed,
123125
MultiModuleWithPrintMetadata,
@@ -126,24 +128,49 @@ pub enum SpirvBuilderError {
126128
MetadataFileMalformed(serde_json::Error),
127129
}
128130

131+
const SPIRV_TARGET_PREFIX: &str = "spirv-unknown-";
132+
129133
impl fmt::Display for SpirvBuilderError {
130134
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
131135
match self {
132-
SpirvBuilderError::CratePathDoesntExist(path) => {
133-
write!(f, "Crate path {} does not exist", path.display())
136+
Self::NonSpirvTarget { target } => {
137+
write!(
138+
f,
139+
"expected `{SPIRV_TARGET_PREFIX}...` target, found `{target}`"
140+
)
134141
}
135-
SpirvBuilderError::BuildFailed => f.write_str("Build failed"),
136-
SpirvBuilderError::MultiModuleWithPrintMetadata => f.write_str(
137-
"Multi-module build cannot be used with print_metadata = MetadataPrintout::Full",
138-
),
139-
SpirvBuilderError::WatchWithPrintMetadata => {
140-
f.write_str("Watching within build scripts will prevent build completion")
142+
Self::UnsupportedSpirvTargetEnv { target_env } if target_env.starts_with("opencl") => {
143+
write!(
144+
f,
145+
"OpenCL targets like `{SPIRV_TARGET_PREFIX}-{target_env}` are not supported"
146+
)
147+
}
148+
Self::UnsupportedSpirvTargetEnv { target_env } if target_env.starts_with("webgpu") => {
149+
write!(
150+
f,
151+
"WebGPU targets like `{SPIRV_TARGET_PREFIX}-{target_env}` are not supported, \
152+
consider using `{SPIRV_TARGET_PREFIX}-vulkan1.0` instead"
153+
)
141154
}
142-
SpirvBuilderError::MetadataFileMissing(_) => {
143-
f.write_str("Multi-module metadata file missing")
155+
Self::UnsupportedSpirvTargetEnv { target_env } => {
156+
write!(
157+
f,
158+
"SPIR-V target `{SPIRV_TARGET_PREFIX}-{target_env}` is not supported"
159+
)
160+
}
161+
Self::CratePathDoesntExist(path) => {
162+
write!(f, "crate path {} does not exist", path.display())
163+
}
164+
Self::BuildFailed => f.write_str("build failed"),
165+
Self::MultiModuleWithPrintMetadata => f.write_str(
166+
"multi-module build cannot be used with print_metadata = MetadataPrintout::Full",
167+
),
168+
Self::WatchWithPrintMetadata => {
169+
f.write_str("watching within build scripts will prevent build completion")
144170
}
145-
SpirvBuilderError::MetadataFileMalformed(_) => {
146-
f.write_str("Unable to parse multi-module metadata file")
171+
Self::MetadataFileMissing(_) => f.write_str("multi-module metadata file missing"),
172+
Self::MetadataFileMalformed(_) => {
173+
f.write_str("unable to parse multi-module metadata file")
147174
}
148175
}
149176
}
@@ -453,6 +480,31 @@ impl SpirvBuilder {
453480
}
454481

455482
pub(crate) fn validate_running_conditions(&mut self) -> Result<(), SpirvBuilderError> {
483+
let target_env = self
484+
.target
485+
.strip_prefix(SPIRV_TARGET_PREFIX)
486+
.ok_or_else(|| SpirvBuilderError::NonSpirvTarget {
487+
target: self.target.clone(),
488+
})?;
489+
// HACK(eddyb) used only to split the full list into groups.
490+
#[allow(clippy::match_same_arms)]
491+
match target_env {
492+
// HACK(eddyb) hardcoded list to avoid checking if the JSON file
493+
// for a particular target exists (and sanitizing strings for paths).
494+
//
495+
// FIXME(eddyb) consider moving this list, or even `target-specs`,
496+
// into `rustc_codegen_spirv_types`'s code/source.
497+
"spv1.0" | "spv1.1" | "spv1.2" | "spv1.3" | "spv1.4" | "spv1.5" => {}
498+
"opengl4.0" | "opengl4.1" | "opengl4.2" | "opengl4.3" | "opengl4.5" => {}
499+
"vulkan1.0" | "vulkan1.1" | "vulkan1.1spv1.4" | "vulkan1.2" => {}
500+
501+
_ => {
502+
return Err(SpirvBuilderError::UnsupportedSpirvTargetEnv {
503+
target_env: target_env.into(),
504+
});
505+
}
506+
}
507+
456508
if (self.print_metadata == MetadataPrintout::Full) && self.multimodule {
457509
return Err(SpirvBuilderError::MultiModuleWithPrintMetadata);
458510
}
@@ -469,8 +521,10 @@ impl SpirvBuilder {
469521
at: &Path,
470522
) -> Result<CompileResult, SpirvBuilderError> {
471523
let metadata_contents = File::open(at).map_err(SpirvBuilderError::MetadataFileMissing)?;
472-
let metadata: CompileResult = serde_json::from_reader(BufReader::new(metadata_contents))
473-
.map_err(SpirvBuilderError::MetadataFileMalformed)?;
524+
// FIXME(eddyb) move this functionality into `rustc_codegen_spirv_types`.
525+
let metadata: CompileResult =
526+
rustc_codegen_spirv_types::serde_json::from_reader(BufReader::new(metadata_contents))
527+
.map_err(SpirvBuilderError::MetadataFileMalformed)?;
474528
match &metadata.module {
475529
ModuleResult::SingleModule(spirv_module) => {
476530
assert!(!self.multimodule);
@@ -691,10 +745,16 @@ fn invoke_rustc(builder: &SpirvBuilder) -> Result<PathBuf, SpirvBuilderError> {
691745
"-Zbuild-std-features=compiler-builtins-mem",
692746
"--profile",
693747
profile,
694-
"--target",
695-
&*builder.target,
696748
]);
697749

750+
// FIXME(eddyb) consider moving `target-specs` into `rustc_codegen_spirv_types`.
751+
// FIXME(eddyb) consider the `RUST_TARGET_PATH` env var alternative.
752+
cargo.arg("--target").arg(
753+
Path::new(env!("CARGO_MANIFEST_DIR"))
754+
.join("target-specs")
755+
.join(&format!("{}.json", builder.target)),
756+
);
757+
698758
// NOTE(eddyb) see above how this is computed and why it might be missing.
699759
if let Some(target_dir) = target_dir {
700760
cargo.arg("--target-dir").arg(target_dir);
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"allows-weak-linkage": false,
3+
"arch": "spirv",
4+
"crt-objects-fallback": "false",
5+
"crt-static-allows-dylibs": true,
6+
"data-layout": "e-m:e-p:32:32:32-i64:64-n8:16:32:64",
7+
"description": null,
8+
"dll-prefix": "",
9+
"dll-suffix": ".spv.json",
10+
"dynamic-linking": true,
11+
"emit-debug-gdb-scripts": false,
12+
"env": "opengl4.0",
13+
"linker-flavor": "unix",
14+
"linker-is-gnu": false,
15+
"llvm-target": "spirv-unknown-opengl4.0",
16+
"main-needs-argc-argv": false,
17+
"os": "unknown",
18+
"panic-strategy": "abort",
19+
"simd-types-indirect": false,
20+
"target-pointer-width": "32"
21+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"allows-weak-linkage": false,
3+
"arch": "spirv",
4+
"crt-objects-fallback": "false",
5+
"crt-static-allows-dylibs": true,
6+
"data-layout": "e-m:e-p:32:32:32-i64:64-n8:16:32:64",
7+
"description": null,
8+
"dll-prefix": "",
9+
"dll-suffix": ".spv.json",
10+
"dynamic-linking": true,
11+
"emit-debug-gdb-scripts": false,
12+
"env": "opengl4.1",
13+
"linker-flavor": "unix",
14+
"linker-is-gnu": false,
15+
"llvm-target": "spirv-unknown-opengl4.1",
16+
"main-needs-argc-argv": false,
17+
"os": "unknown",
18+
"panic-strategy": "abort",
19+
"simd-types-indirect": false,
20+
"target-pointer-width": "32"
21+
}

0 commit comments

Comments
 (0)