From c3a3b20e3c200aa5ebed1dc5f5605004d1955ed2 Mon Sep 17 00:00:00 2001 From: Ashley Hauck Date: Mon, 29 Mar 2021 17:59:03 +0200 Subject: [PATCH] Add support for outputting multiple modules, one per entry point (#551) * Add multimodule feature * Use -Cllvm-args instead of -Ctarget-feature for multimodule * Fix cargo.toml --- Cargo.lock | 149 +++++------------- Cargo.toml | 1 + crates/rustc_codegen_spirv/Cargo.toml | 1 + .../rustc_codegen_spirv/src/codegen_cx/mod.rs | 50 ++++++ crates/rustc_codegen_spirv/src/lib.rs | 5 +- crates/rustc_codegen_spirv/src/link.rs | 63 ++++++-- crates/rustc_codegen_spirv/src/linker/dce.rs | 3 + crates/rustc_codegen_spirv/src/linker/mod.rs | 66 ++++++-- crates/rustc_codegen_spirv/src/linker/test.rs | 8 +- crates/spirv-builder/src/lib.rs | 39 ++++- examples/multibuilder/Cargo.toml | 16 ++ examples/multibuilder/src/main.rs | 10 ++ examples/shaders/simplest-shader/src/lib.rs | 5 +- 13 files changed, 273 insertions(+), 143 deletions(-) create mode 100644 examples/multibuilder/Cargo.toml create mode 100644 examples/multibuilder/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index 3695114ec6..6aa3be098e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1402,9 +1402,9 @@ dependencies = [ [[package]] name = "memoffset" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "157b4208e3059a8f9e78d559edc658e13df41410cb3ae03979c83130067fdd87" +checksum = "cc14fc54a812b4472b4113facc3e44d099fbc0ea2ce0551fa5c703f8edfbfd38" dependencies = [ "autocfg", ] @@ -1425,17 +1425,17 @@ dependencies = [ [[package]] name = "minifb" -version = "0.19.2" +version = "0.19.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87b4b6a6da61c3003a41e84ee1f6b56e079f835f4d4fce424fc84092b57ad804" +checksum = "7b6e41119d1667465608d36488fa5dcd228057a26c156e25f17f492f38435124" dependencies = [ "cc", "orbclient", "raw-window-handle", "tempfile", - "wayland-client 0.27.0", - "wayland-cursor 0.27.0", - "wayland-protocols 0.27.0", + "wayland-client", + "wayland-cursor", + "wayland-protocols", "winapi 0.3.9", "x11-dl", "xkb", @@ -1513,6 +1513,13 @@ dependencies = [ "spirv-std", ] +[[package]] +name = "multibuilder" +version = "0.1.0" +dependencies = [ + "spirv-builder", +] + [[package]] name = "naga" version = "0.3.2" @@ -1585,19 +1592,6 @@ dependencies = [ "winapi 0.3.9", ] -[[package]] -name = "nix" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50e4785f2c3b7589a0d0c1dd60285e1188adac4006e8abd6dd578e1567027363" -dependencies = [ - "bitflags", - "cc", - "cfg-if 0.1.10", - "libc", - "void", -] - [[package]] name = "nix" version = "0.18.0" @@ -2110,6 +2104,7 @@ dependencies = [ "pretty_assertions", "rspirv", "rustc-demangle", + "sanitize-filename", "serde", "serde_json", "smallvec", @@ -2169,6 +2164,16 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "sanitize-filename" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf18934a12018228c5b55a6dae9df5d0641e3566b3630cb46cc55564068e7c2f" +dependencies = [ + "lazy_static", + "regex", +] + [[package]] name = "scoped-tls" version = "1.0.0" @@ -2314,9 +2319,9 @@ dependencies = [ "log", "memmap2", "nix 0.18.0", - "wayland-client 0.28.5", - "wayland-cursor 0.28.5", - "wayland-protocols 0.28.5", + "wayland-client", + "wayland-cursor", + "wayland-protocols", ] [[package]] @@ -2758,17 +2763,11 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" -[[package]] -name = "void" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" - [[package]] name = "walkdir" -version = "2.3.1" +version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d" +checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" dependencies = [ "same-file", "winapi 0.3.9", @@ -2847,21 +2846,6 @@ version = "0.2.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e7811dd7f9398f14cc76efd356f98f03aa30419dea46aa810d71e819fc97158" -[[package]] -name = "wayland-client" -version = "0.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab702fefbcd6d6f67fb5816e3a89a3b5a42a94290abbc015311c9a30d1068ae4" -dependencies = [ - "bitflags", - "downcast-rs", - "libc", - "nix 0.17.0", - "wayland-commons 0.27.0", - "wayland-scanner 0.27.0", - "wayland-sys 0.27.0", -] - [[package]] name = "wayland-client" version = "0.28.5" @@ -2873,21 +2857,9 @@ dependencies = [ "libc", "nix 0.20.0", "scoped-tls", - "wayland-commons 0.28.5", - "wayland-scanner 0.28.5", - "wayland-sys 0.28.5", -] - -[[package]] -name = "wayland-commons" -version = "0.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e972e9336ad5a9dd861b4e21ff35ad71d3e5c6b4803d65c39913612f851b95f1" -dependencies = [ - "nix 0.17.0", - "once_cell", - "smallvec", - "wayland-sys 0.27.0", + "wayland-commons", + "wayland-scanner", + "wayland-sys", ] [[package]] @@ -2899,18 +2871,7 @@ dependencies = [ "nix 0.20.0", "once_cell", "smallvec", - "wayland-sys 0.28.5", -] - -[[package]] -name = "wayland-cursor" -version = "0.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "539f346e1a3f706f38c8ccbe1196001e2fb1c9b3e6b605c27d665db2f5b60d41" -dependencies = [ - "nix 0.17.0", - "wayland-client 0.27.0", - "xcursor", + "wayland-sys", ] [[package]] @@ -2920,22 +2881,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b37e5455ec72f5de555ec39b5c3704036ac07c2ecd50d0bffe02d5fe2d4e65ab" dependencies = [ "nix 0.20.0", - "wayland-client 0.28.5", + "wayland-client", "xcursor", ] -[[package]] -name = "wayland-protocols" -version = "0.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3d6fc54b17b98b5083bc21ae3a30e6d75cb4b01647360e4c3a04648bcf8781d" -dependencies = [ - "bitflags", - "wayland-client 0.27.0", - "wayland-commons 0.27.0", - "wayland-scanner 0.27.0", -] - [[package]] name = "wayland-protocols" version = "0.28.5" @@ -2943,20 +2892,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95df3317872bcf9eec096c864b69aa4769a1d5d6291a5b513f8ba0af0efbd52c" dependencies = [ "bitflags", - "wayland-client 0.28.5", - "wayland-commons 0.28.5", - "wayland-scanner 0.28.5", -] - -[[package]] -name = "wayland-scanner" -version = "0.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "030f56009d932bd9400bb472764fea8109be1b0fc482d9cd75496c943ac30328" -dependencies = [ - "proc-macro2", - "quote", - "xml-rs", + "wayland-client", + "wayland-commons", + "wayland-scanner", ] [[package]] @@ -2970,15 +2908,6 @@ dependencies = [ "xml-rs", ] -[[package]] -name = "wayland-sys" -version = "0.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bdeffbbb474477dfa2acb45ac7479e5fe8f741c64ab032c5d11b94d07edc269" -dependencies = [ - "pkg-config", -] - [[package]] name = "wayland-sys" version = "0.28.5" @@ -3150,7 +3079,7 @@ dependencies = [ "raw-window-handle", "smithay-client-toolkit", "wasm-bindgen", - "wayland-client 0.28.5", + "wayland-client", "web-sys", "winapi 0.3.9", "x11-dl", diff --git a/Cargo.toml b/Cargo.toml index 8fcefd6a6f..73a106efa4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ members = [ "examples/shaders/simplest-shader", "examples/shaders/compute-shader", "examples/shaders/mouse-shader", + "examples/multibuilder", "crates/rustc_codegen_spirv", "crates/spirv-builder", diff --git a/crates/rustc_codegen_spirv/Cargo.toml b/crates/rustc_codegen_spirv/Cargo.toml index 21841d4393..e7685cd971 100644 --- a/crates/rustc_codegen_spirv/Cargo.toml +++ b/crates/rustc_codegen_spirv/Cargo.toml @@ -39,6 +39,7 @@ bimap = "0.6" indexmap = "1.6.0" rspirv = { git = "https://github.com/gfx-rs/rspirv.git", rev = "ee1e913" } rustc-demangle = "0.1.18" +sanitize-filename = "0.3" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" smallvec = "1.6.1" diff --git a/crates/rustc_codegen_spirv/src/codegen_cx/mod.rs b/crates/rustc_codegen_spirv/src/codegen_cx/mod.rs index 83ed6378ea..6ae32b2a71 100644 --- a/crates/rustc_codegen_spirv/src/codegen_cx/mod.rs +++ b/crates/rustc_codegen_spirv/src/codegen_cx/mod.rs @@ -33,6 +33,7 @@ use std::cell::{Cell, RefCell}; use std::collections::HashMap; use std::iter::once; use std::rc::Rc; +use std::str::FromStr; pub struct CodegenCx<'tcx> { pub tcx: TyCtxt<'tcx>, @@ -69,6 +70,8 @@ pub struct CodegenCx<'tcx> { /// Some runtimes (e.g. intel-compute-runtime) disallow atomics on i8 and i16, even though it's allowed by the spec. /// This enables/disables them. pub i8_i16_atomics_allowed: bool, + + pub codegen_args: CodegenArgs, } impl<'tcx> CodegenCx<'tcx> { @@ -102,6 +105,7 @@ impl<'tcx> CodegenCx<'tcx> { tcx.sess.err(&format!("Unknown feature {}", feature)); } } + let codegen_args = CodegenArgs::from_session(tcx.sess); Self { tcx, codegen_unit, @@ -120,6 +124,7 @@ impl<'tcx> CodegenCx<'tcx> { panic_fn_id: Default::default(), panic_bounds_check_fn_id: Default::default(), i8_i16_atomics_allowed: false, + codegen_args, } } @@ -256,6 +261,51 @@ impl<'tcx> CodegenCx<'tcx> { } } +pub struct CodegenArgs { + pub module_output_type: ModuleOutputType, +} + +impl CodegenArgs { + pub fn from_session(sess: &Session) -> Self { + match CodegenArgs::parse(&sess.opts.cg.llvm_args) { + Ok(ok) => ok, + Err(err) => sess.fatal(&format!("Unable to parse llvm-args: {}", err)), + } + } + + pub fn parse(args: &[String]) -> Result { + use rustc_session::getopts; + let mut opts = getopts::Options::new(); + opts.optopt( + "", + "module-output", + "single output or multiple output", + "[single|multiple]", + ); + let matches = opts.parse(args)?; + let module_output_type = + matches.opt_get_default("module-output", ModuleOutputType::Single)?; + Ok(Self { module_output_type }) + } +} + +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum ModuleOutputType { + Single, + Multiple, +} + +impl FromStr for ModuleOutputType { + type Err = rustc_session::getopts::Fail; + fn from_str(s: &str) -> Result { + match s { + "single" => Ok(Self::Single), + "multiple" => Ok(Self::Multiple), + v => Err(Self::Err::UnrecognizedOption(v.to_string())), + } + } +} + impl<'tcx> BackendTypes for CodegenCx<'tcx> { type Value = SpirvValue; type Function = SpirvValue; diff --git a/crates/rustc_codegen_spirv/src/lib.rs b/crates/rustc_codegen_spirv/src/lib.rs index 3c2564cd0d..2e71725a55 100644 --- a/crates/rustc_codegen_spirv/src/lib.rs +++ b/crates/rustc_codegen_spirv/src/lib.rs @@ -118,7 +118,7 @@ mod spirv_type_constraints; mod symbols; use builder::Builder; -use codegen_cx::CodegenCx; +use codegen_cx::{CodegenArgs, CodegenCx, ModuleOutputType}; pub use rspirv; use rspirv::binary::Assemble; use rustc_ast::expand::allocator::AllocatorKind; @@ -380,6 +380,8 @@ impl CodegenBackend for SpirvCodegenBackend { ) -> Result<(), ErrorReported> { // TODO: Can we merge this sym with the one in symbols.rs? let legalize = !sess.target_features.contains(&Symbol::intern("kernel")); + let codegen_args = CodegenArgs::from_session(sess); + let emit_multiple_modules = codegen_args.module_output_type == ModuleOutputType::Multiple; let timer = sess.timer("link_crate"); link::link( @@ -388,6 +390,7 @@ impl CodegenBackend for SpirvCodegenBackend { outputs, &codegen_results.crate_name.as_str(), legalize, + emit_multiple_modules, ); drop(timer); diff --git a/crates/rustc_codegen_spirv/src/link.rs b/crates/rustc_codegen_spirv/src/link.rs index e4336a9466..0ed9539188 100644 --- a/crates/rustc_codegen_spirv/src/link.rs +++ b/crates/rustc_codegen_spirv/src/link.rs @@ -13,11 +13,11 @@ use rustc_session::config::{CrateType, DebugInfo, Lto, OptLevel, OutputFilenames use rustc_session::output::{check_file_is_writeable, invalid_output_for_target, out_filename}; use rustc_session::utils::NativeLibKind; use rustc_session::Session; -use std::collections::HashSet; +use std::collections::{HashMap, HashSet}; use std::env; use std::ffi::{CString, OsStr}; use std::fs::File; -use std::io::{Read, Write}; +use std::io::{BufWriter, Read, Write}; use std::path::{Path, PathBuf}; use std::sync::Arc; use tar::{Archive, Builder, Header}; @@ -28,6 +28,7 @@ pub fn link<'a>( outputs: &OutputFilenames, crate_name: &str, legalize: bool, + emit_multiple_modules: bool, ) { let output_metadata = sess.opts.output_types.contains_key(&OutputType::Metadata); for &crate_type in sess.crate_types().iter() { @@ -60,9 +61,14 @@ pub fn link<'a>( CrateType::Rlib => { link_rlib(sess, codegen_results, &out_filename); } - CrateType::Executable | CrateType::Cdylib | CrateType::Dylib => { - link_exe(sess, crate_type, &out_filename, codegen_results, legalize) - } + CrateType::Executable | CrateType::Cdylib | CrateType::Dylib => link_exe( + sess, + crate_type, + &out_filename, + codegen_results, + legalize, + emit_multiple_modules, + ), other => sess.err(&format!("CrateType {:?} not supported yet", other)), } } @@ -104,6 +110,7 @@ fn link_exe( out_filename: &Path, codegen_results: &CodegenResults, legalize: bool, + emit_multiple_modules: bool, ) { let mut objects = Vec::new(); let mut rlibs = Vec::new(); @@ -122,8 +129,34 @@ fn link_exe( codegen_results, ); - let spv_binary = do_link(sess, &objects, &rlibs, legalize); + let spv_binary = do_link(sess, &objects, &rlibs, legalize, emit_multiple_modules); + use rspirv::binary::Assemble; + match spv_binary { + linker::LinkResult::SingleModule(spv_binary) => { + post_link_single_module(sess, spv_binary.assemble(), out_filename); + } + linker::LinkResult::MultipleModules(map) => { + let mut root_file_name = out_filename.file_name().unwrap().to_owned(); + root_file_name.push(".dir"); + let out_dir = out_filename.with_file_name(root_file_name); + if !out_dir.is_dir() { + std::fs::create_dir_all(&out_dir).unwrap(); + } + let mut hashmap = HashMap::new(); + for (name, spv_binary) in map { + let mut module_filename = out_dir.clone(); + module_filename.push(sanitize_filename::sanitize(&name)); + post_link_single_module(sess, spv_binary.assemble(), &module_filename); + hashmap.insert(name, module_filename); + } + let file = File::create(out_filename).unwrap(); + serde_json::to_writer(BufWriter::new(file), &hashmap).unwrap(); + } + } +} + +fn post_link_single_module(sess: &Session, spv_binary: Vec, out_filename: &Path) { if let Ok(ref path) = std::env::var("DUMP_POST_LINK") { File::create(path) .unwrap() @@ -378,7 +411,13 @@ pub fn read_metadata(rlib: &Path) -> Result { /// This is the actual guts of linking: the rest of the link-related functions are just digging through rustc's /// shenanigans to collect all the object files we need to link. -fn do_link(sess: &Session, objects: &[PathBuf], rlibs: &[PathBuf], legalize: bool) -> Vec { +fn do_link( + sess: &Session, + objects: &[PathBuf], + rlibs: &[PathBuf], + legalize: bool, + emit_multiple_modules: bool, +) -> linker::LinkResult { fn load(bytes: &[u8]) -> rspirv::dr::Module { let mut loader = rspirv::dr::Loader::new(); rspirv::binary::parse_bytes(&bytes, &mut loader).unwrap(); @@ -407,6 +446,7 @@ fn do_link(sess: &Session, objects: &[PathBuf], rlibs: &[PathBuf], legalize: boo } if let Ok(ref path) = env::var("DUMP_PRE_LINK") { + use rspirv::binary::Assemble; let path = Path::new(path); if path.is_file() { std::fs::remove_file(path).unwrap(); @@ -429,21 +469,18 @@ fn do_link(sess: &Session, objects: &[PathBuf], rlibs: &[PathBuf], legalize: boo mem2reg: legalize, structurize: env::var("NO_STRUCTURIZE").is_err(), use_new_structurizer: env::var("OLD_STRUCTURIZER").is_err(), + emit_multiple_modules, }; let link_result = linker::link(sess, modules, &options); - let assembled = match link_result { + match link_result { Ok(v) => v, Err(rustc_errors::ErrorReported) => { sess.abort_if_errors(); bug!("Linker errored, but no error reported") } - }; - - // And finally write out the linked binary. - use rspirv::binary::Assemble; - assembled.assemble() + } } /// As of right now, this is essentially a no-op, just plumbing through all the files. diff --git a/crates/rustc_codegen_spirv/src/linker/dce.rs b/crates/rustc_codegen_spirv/src/linker/dce.rs index e095ef72fd..5ec7e05a76 100644 --- a/crates/rustc_codegen_spirv/src/linker/dce.rs +++ b/crates/rustc_codegen_spirv/src/linker/dce.rs @@ -73,6 +73,9 @@ fn kill_unrooted(module: &mut Module, rooted: &HashSet) { module .ext_inst_imports .retain(|inst| is_rooted(inst, rooted)); + module + .execution_modes + .retain(|inst| is_rooted(inst, rooted)); module.debugs.retain(|inst| is_rooted(inst, rooted)); module.annotations.retain(|inst| is_rooted(inst, rooted)); module diff --git a/crates/rustc_codegen_spirv/src/linker/mod.rs b/crates/rustc_codegen_spirv/src/linker/mod.rs index f09684338b..f58cbe5e09 100644 --- a/crates/rustc_codegen_spirv/src/linker/mod.rs +++ b/crates/rustc_codegen_spirv/src/linker/mod.rs @@ -30,6 +30,12 @@ pub struct Options { pub mem2reg: bool, pub structurize: bool, pub use_new_structurizer: bool, + pub emit_multiple_modules: bool, +} + +pub enum LinkResult { + SingleModule(Module), + MultipleModules(HashMap), } fn id(header: &mut ModuleHeader) -> Word { @@ -70,9 +76,7 @@ fn apply_rewrite_rules(rewrite_rules: &HashMap, blocks: &mut [Block] } } -// Sess needs to be Option because linker tests call this method, and linker tests can't synthesize -// a test Session (not sure how to do that). -pub fn link(sess: &Session, mut inputs: Vec, opts: &Options) -> Result { +pub fn link(sess: &Session, mut inputs: Vec, opts: &Options) -> Result { let mut output = { let _timer = sess.timer("link_merge"); // shift all the ids @@ -241,18 +245,54 @@ pub fn link(sess: &Session, mut inputs: Vec, opts: &Options) -> Result> = match output { + LinkResult::SingleModule(ref mut m) => Box::new(std::iter::once(m)), + LinkResult::MultipleModules(ref mut m) => Box::new(m.values_mut()), }; + for output in output_module_iter { + if let Ok(ref path) = std::env::var("DUMP_POST_SPLIT") { + use rspirv::binary::Assemble; + use std::fs::File; + use std::io::Write; + + File::create(path) + .unwrap() + .write_all(spirv_tools::binary::from_binary(&output.assemble())) + .unwrap(); + } + if opts.dce && opts.emit_multiple_modules { + let _timer = sess.timer("link_dce_2"); + dce::dce(output); + } + { + let _timer = sess.timer("link_remove_extra_capabilities"); + capability_computation::remove_extra_capabilities(output); + capability_computation::remove_extra_extensions(output); + } + + if opts.compact_ids { + let _timer = sess.timer("link_compact_ids"); + // compact the ids https://github.com/KhronosGroup/SPIRV-Tools/blob/e02f178a716b0c3c803ce31b9df4088596537872/source/opt/compact_ids_pass.cpp#L43 + output.header.as_mut().unwrap().bound = simple_passes::compact_ids(output); + }; + } - // output the module Ok(output) } diff --git a/crates/rustc_codegen_spirv/src/linker/test.rs b/crates/rustc_codegen_spirv/src/linker/test.rs index 663a85d052..3e513a0a68 100644 --- a/crates/rustc_codegen_spirv/src/linker/test.rs +++ b/crates/rustc_codegen_spirv/src/linker/test.rs @@ -1,4 +1,4 @@ -use super::{link, Options}; +use super::{link, LinkResult, Options}; use pipe::pipe; use rspirv::dr::{Loader, Module}; use rustc_driver::handle_options; @@ -95,10 +95,14 @@ fn assemble_and_link(binaries: &[&[u8]]) -> Result { mem2reg: false, structurize: false, use_new_structurizer: false, + emit_multiple_modules: false, }, ); assert_eq!(compiler.session().has_errors(), res.is_err()); - res + res.map(|res| match res { + LinkResult::SingleModule(m) => m, + LinkResult::MultipleModules(_) => unreachable!(), + }) }) .map_err(|_e| thread.join().unwrap()) } diff --git a/crates/spirv-builder/src/lib.rs b/crates/spirv-builder/src/lib.rs index a7f301fb29..8253884d0f 100644 --- a/crates/spirv-builder/src/lib.rs +++ b/crates/spirv-builder/src/lib.rs @@ -64,18 +64,32 @@ use std::collections::HashMap; use std::env; use std::error::Error; use std::fmt; +use std::fs::File; +use std::io::BufReader; use std::path::{Path, PathBuf}; use std::process::{Command, Stdio}; #[derive(Debug)] pub enum SpirvBuilderError { BuildFailed, + MultiModuleWithPrintMetadata, + MetadataFileMissing(std::io::Error), + MetadataFileMalformed(serde_json::Error), } impl fmt::Display for SpirvBuilderError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { SpirvBuilderError::BuildFailed => f.write_str("Build failed"), + SpirvBuilderError::MultiModuleWithPrintMetadata => { + f.write_str("Multi-module build cannot be used with print_metadata = true") + } + SpirvBuilderError::MetadataFileMissing(_) => { + f.write_str("Multi-module metadata file missing") + } + SpirvBuilderError::MetadataFileMalformed(_) => { + f.write_str("Unable to parse multi-module metadata file") + } } } } @@ -134,13 +148,25 @@ impl SpirvBuilder { /// you usually don't have to inspect the path, as the environment variable will already be /// set. pub fn build(self) -> Result { - let spirv_module = invoke_rustc(&self)?; + let spirv_module = invoke_rustc(&self, false)?; let env_var = spirv_module.file_name().unwrap().to_str().unwrap(); if self.print_metadata { println!("cargo:rustc-env={}={}", env_var, spirv_module.display()); } Ok(spirv_module) } + + pub fn build_multimodule(self) -> Result, SpirvBuilderError> { + if self.print_metadata { + return Err(SpirvBuilderError::MultiModuleWithPrintMetadata); + } + let metadata_file = invoke_rustc(&self, true)?; + let metadata_contents = + File::open(metadata_file).map_err(SpirvBuilderError::MetadataFileMissing)?; + let metadata = serde_json::from_reader(BufReader::new(metadata_contents)) + .map_err(SpirvBuilderError::MetadataFileMalformed)?; + Ok(metadata) + } } // https://github.com/rust-lang/cargo/blob/1857880b5124580c4aeb4e8bc5f1198f491d61b1/src/cargo/util/paths.rs#L29-L52 @@ -175,7 +201,8 @@ fn find_rustc_codegen_spirv() -> PathBuf { panic!("Could not find {} in library path", filename); } -fn invoke_rustc(builder: &SpirvBuilder) -> Result { +// Note: in case of multimodule, returns path to the metadata json +fn invoke_rustc(builder: &SpirvBuilder, multimodule: bool) -> Result { // Okay, this is a little bonkers: in a normal world, we'd have the user clone // rustc_codegen_spirv and pass in the path to it, and then we'd invoke cargo to build it, grab // the resulting .so, and pass it into -Z codegen-backend. But that's really gross: the user @@ -205,10 +232,16 @@ fn invoke_rustc(builder: &SpirvBuilder) -> Result { } else { format!(" -C target-feature={}", target_features.join(",")) }; + let llvm_args = if multimodule { + " -C llvm-args=--module-output=multiple" + } else { + "" + }; let rustflags = format!( - "-Z codegen-backend={} -Z symbol-mangling-version=v0{}", + "-Z codegen-backend={} -Z symbol-mangling-version=v0{}{}", rustc_codegen_spirv.display(), feature_flag, + llvm_args, ); let mut cargo = Command::new("cargo"); cargo.args(&[ diff --git a/examples/multibuilder/Cargo.toml b/examples/multibuilder/Cargo.toml new file mode 100644 index 0000000000..00b8d28258 --- /dev/null +++ b/examples/multibuilder/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "multibuilder" +version = "0.1.0" +authors = ["Embark "] +edition = "2018" +license = "MIT OR Apache-2.0" +publish = false + +# See rustc_codegen_spirv/Cargo.toml for details on these features +[features] +default = ["use-compiled-tools"] +use-installed-tools = ["spirv-builder/use-installed-tools"] +use-compiled-tools = ["spirv-builder/use-compiled-tools"] + +[dependencies] +spirv-builder = { path = "../../crates/spirv-builder", default-features = false } diff --git a/examples/multibuilder/src/main.rs b/examples/multibuilder/src/main.rs new file mode 100644 index 0000000000..9b3bd5e67e --- /dev/null +++ b/examples/multibuilder/src/main.rs @@ -0,0 +1,10 @@ +use spirv_builder::SpirvBuilder; + +fn main() { + let result = SpirvBuilder::new("../shaders/sky-shader") + .print_metadata(false) + .spirv_version(1, 0) + .build_multimodule() + .unwrap(); + println!("{:#?}", result); +} diff --git a/examples/shaders/simplest-shader/src/lib.rs b/examples/shaders/simplest-shader/src/lib.rs index 0d9aebe17f..b602a12d4b 100644 --- a/examples/shaders/simplest-shader/src/lib.rs +++ b/examples/shaders/simplest-shader/src/lib.rs @@ -18,7 +18,10 @@ pub fn main_fs(output: &mut Vec4) { } #[spirv(vertex)] -pub fn main_vs(#[spirv(vertex_index)] vert_id: i32, #[spirv(position)] out_pos: &mut Vec4) { +pub fn main_vs( + #[spirv(vertex_index)] vert_id: i32, + #[spirv(position, invariant)] out_pos: &mut Vec4, +) { *out_pos = vec4( (vert_id - 1) as f32, ((vert_id & 1) * 2 - 1) as f32,