mirror of
https://github.com/EmbarkStudios/rust-gpu.git
synced 2024-11-25 08:14:12 +00:00
Synthesize Session for tests, instead of Option<Session> (#228)
Also remove LinkerError and use rustc error reporting directly instead
This commit is contained in:
parent
47d23ba800
commit
86da42f2d7
42
Cargo.lock
generated
42
Cargo.lock
generated
@ -393,6 +393,16 @@ dependencies = [
|
||||
"objc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-channel"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b153fe7cbef478c567df0f972e02e6d736db11affe43dfc9c56a9374d1adfb87"
|
||||
dependencies = [
|
||||
"crossbeam-utils 0.7.2",
|
||||
"maybe-uninit",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-channel"
|
||||
version = "0.5.0"
|
||||
@ -400,7 +410,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dca26ee1f8d361640700bde38b2c37d8c22b3ce2d360e1fc1c74ea4b0aa7d775"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"crossbeam-utils",
|
||||
"crossbeam-utils 0.8.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -411,7 +421,7 @@ checksum = "94af6efb46fef72616855b036a624cf27ba656ffc9be1b9a3c931cfc7749a9a9"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-utils",
|
||||
"crossbeam-utils 0.8.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -422,12 +432,23 @@ checksum = "ec0f606a85340376eef0d6d8fec399e6d4a544d648386c6645eb6d0653b27d9f"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"const_fn",
|
||||
"crossbeam-utils",
|
||||
"crossbeam-utils 0.8.0",
|
||||
"lazy_static",
|
||||
"memoffset",
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8"
|
||||
dependencies = [
|
||||
"autocfg 1.0.1",
|
||||
"cfg-if 0.1.10",
|
||||
"lazy_static",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.0"
|
||||
@ -1510,6 +1531,15 @@ version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||
|
||||
[[package]]
|
||||
name = "pipe"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bcd11e042e056991b5df9c0c5ae6bd0cce219b74294c40f65b89f40f7030106c"
|
||||
dependencies = [
|
||||
"crossbeam-channel 0.4.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.19"
|
||||
@ -1813,9 +1843,9 @@ version = "1.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ab346ac5921dc62ffa9f89b7a773907511cdfa5490c572ae9be1be33e8afa4a"
|
||||
dependencies = [
|
||||
"crossbeam-channel",
|
||||
"crossbeam-channel 0.5.0",
|
||||
"crossbeam-deque",
|
||||
"crossbeam-utils",
|
||||
"crossbeam-utils 0.8.0",
|
||||
"lazy_static",
|
||||
"num_cpus",
|
||||
]
|
||||
@ -1900,12 +1930,12 @@ name = "rustc_codegen_spirv"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bimap",
|
||||
"pipe",
|
||||
"pretty_assertions",
|
||||
"rspirv 0.7.0 (git+https://github.com/gfx-rs/rspirv.git?rev=f11f8797bd4df2d1d22cf10767b39a5119c57551)",
|
||||
"spirv-tools",
|
||||
"tar",
|
||||
"tempfile",
|
||||
"thiserror",
|
||||
"topological-sort",
|
||||
]
|
||||
|
||||
|
@ -30,7 +30,6 @@ use-compiled-tools = ["spirv-tools/use-compiled-tools"]
|
||||
bimap = "0.5"
|
||||
rspirv = { git = "https://github.com/gfx-rs/rspirv.git", rev = "f11f8797bd4df2d1d22cf10767b39a5119c57551" }
|
||||
tar = "0.4.30"
|
||||
thiserror = "1.0.20"
|
||||
topological-sort = "0.1"
|
||||
|
||||
[dependencies.spirv-tools]
|
||||
@ -39,5 +38,6 @@ path = "../spirv-tools"
|
||||
default-features = false
|
||||
|
||||
[dev-dependencies]
|
||||
pipe = "0.3"
|
||||
pretty_assertions = "0.6"
|
||||
tempfile = "3.1"
|
||||
|
@ -113,6 +113,7 @@ use rustc_middle::mir::mono::{Linkage, MonoItem, Visibility};
|
||||
use rustc_middle::ty::print::with_no_trimmed_paths;
|
||||
use rustc_middle::ty::query::Providers;
|
||||
use rustc_middle::ty::{InstanceDef, TyCtxt};
|
||||
use rustc_mir::util::write_mir_pretty;
|
||||
use rustc_session::config::{self, OptLevel, OutputFilenames, OutputType};
|
||||
use rustc_session::Session;
|
||||
use rustc_span::Symbol;
|
||||
@ -120,22 +121,24 @@ use rustc_target::spec::abi::Abi;
|
||||
use rustc_target::spec::{LinkerFlavor, PanicStrategy, Target, TargetOptions, TargetTriple};
|
||||
use std::any::Any;
|
||||
use std::env;
|
||||
use std::fs::{create_dir_all, File};
|
||||
use std::io::Cursor;
|
||||
use std::io::Write;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::{fs::File, io::Write, sync::Arc};
|
||||
use std::sync::Arc;
|
||||
|
||||
fn dump_mir<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
mono_items: &[(MonoItem<'_>, (Linkage, Visibility))],
|
||||
path: &Path,
|
||||
) {
|
||||
std::fs::create_dir_all(path.parent().unwrap()).unwrap();
|
||||
create_dir_all(path.parent().unwrap()).unwrap();
|
||||
let mut file = File::create(path).unwrap();
|
||||
for &(mono_item, (_, _)) in mono_items {
|
||||
if let MonoItem::Fn(instance) = mono_item {
|
||||
if matches!(instance.def, InstanceDef::Item(_)) {
|
||||
let mut mir = ::std::io::Cursor::new(Vec::new());
|
||||
if rustc_mir::util::write_mir_pretty(tcx, Some(instance.def_id()), &mut mir).is_ok()
|
||||
{
|
||||
let mut mir = Cursor::new(Vec::new());
|
||||
if write_mir_pretty(tcx, Some(instance.def_id()), &mut mir).is_ok() {
|
||||
writeln!(file, "{}", String::from_utf8(mir.into_inner()).unwrap()).unwrap()
|
||||
}
|
||||
}
|
||||
@ -270,8 +273,8 @@ impl CodegenBackend for SpirvCodegenBackend {
|
||||
|
||||
fn codegen_crate(
|
||||
&self,
|
||||
tcx: rustc_middle::ty::TyCtxt<'_>,
|
||||
metadata: rustc_middle::middle::cstore::EncodedMetadata,
|
||||
tcx: TyCtxt<'_>,
|
||||
metadata: EncodedMetadata,
|
||||
need_metadata_module: bool,
|
||||
) -> Box<dyn Any> {
|
||||
Box::new(rustc_codegen_ssa::base::codegen_crate(
|
||||
@ -550,7 +553,7 @@ struct DumpModuleOnPanic<'a, 'cx, 'tcx> {
|
||||
impl Drop for DumpModuleOnPanic<'_, '_, '_> {
|
||||
fn drop(&mut self) {
|
||||
if std::thread::panicking() {
|
||||
let path: &std::path::Path = self.path.as_ref();
|
||||
let path: &Path = self.path.as_ref();
|
||||
if path.has_root() {
|
||||
self.cx.builder.dump_module(path);
|
||||
} else {
|
||||
|
@ -386,15 +386,13 @@ fn do_link(sess: &Session, objects: &[PathBuf], rlibs: &[PathBuf], legalize: boo
|
||||
}
|
||||
}
|
||||
|
||||
let mut module_refs = modules.iter_mut().collect::<Vec<_>>();
|
||||
|
||||
if let Ok(ref path) = env::var("DUMP_PRE_LINK") {
|
||||
let path = Path::new(path);
|
||||
if path.is_file() {
|
||||
std::fs::remove_file(path).unwrap();
|
||||
}
|
||||
std::fs::create_dir_all(path).unwrap();
|
||||
for (num, module) in module_refs.iter().enumerate() {
|
||||
for (num, module) in modules.iter().enumerate() {
|
||||
File::create(path.join(format!("mod_{}.spv", num)))
|
||||
.unwrap()
|
||||
.write_all(crate::slice_u32_to_u8(&module.assemble()))
|
||||
@ -412,25 +410,13 @@ fn do_link(sess: &Session, objects: &[PathBuf], rlibs: &[PathBuf], legalize: boo
|
||||
structurize: env::var("NO_STRUCTURIZE").is_err(),
|
||||
};
|
||||
|
||||
let link_result = linker::link(Some(sess), &mut module_refs, &options);
|
||||
let link_result = linker::link(sess, modules, &options);
|
||||
|
||||
let assembled = match link_result {
|
||||
Ok(v) => v,
|
||||
Err(err) => {
|
||||
if let Ok(ref path) = env::var("DUMP_MODULE_ON_PANIC") {
|
||||
let path = Path::new(path);
|
||||
if path.is_file() {
|
||||
std::fs::remove_file(path).unwrap();
|
||||
}
|
||||
std::fs::create_dir_all(path).unwrap();
|
||||
for (num, module) in modules.iter().enumerate() {
|
||||
File::create(path.join(format!("mod_{}.spv", num)))
|
||||
.unwrap()
|
||||
.write_all(crate::slice_u32_to_u8(&module.assemble()))
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
sess.fatal(&format!("Linker error: {}", err))
|
||||
Err(rustc_errors::ErrorReported) => {
|
||||
sess.abort_if_errors();
|
||||
bug!("Linker errored, but no error reported")
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1,10 +1,13 @@
|
||||
use super::{LinkerError, Result};
|
||||
use super::Result;
|
||||
use rspirv::dr::{Instruction, Module};
|
||||
use rspirv::spirv::{Capability, Decoration, LinkageType, Op, Word};
|
||||
use rustc_errors::ErrorReported;
|
||||
use rustc_session::Session;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
pub fn run(module: &mut Module) -> Result<()> {
|
||||
let (rewrite_rules, killed_parameters) = find_import_export_pairs_and_killed_params(module)?;
|
||||
pub fn run(sess: &Session, module: &mut Module) -> Result<()> {
|
||||
let (rewrite_rules, killed_parameters) =
|
||||
find_import_export_pairs_and_killed_params(sess, module)?;
|
||||
kill_linkage_instructions(module, &rewrite_rules);
|
||||
import_kill_annotations_and_debug(module, &rewrite_rules, &killed_parameters);
|
||||
replace_all_uses_with(module, &rewrite_rules);
|
||||
@ -12,6 +15,7 @@ pub fn run(module: &mut Module) -> Result<()> {
|
||||
}
|
||||
|
||||
fn find_import_export_pairs_and_killed_params(
|
||||
sess: &Session,
|
||||
module: &Module,
|
||||
) -> Result<(HashMap<u32, u32>, HashSet<u32>)> {
|
||||
let type_map = get_type_map(module);
|
||||
@ -31,7 +35,8 @@ fn find_import_export_pairs_and_killed_params(
|
||||
};
|
||||
let type_id = *type_map.get(&id).expect("Unexpected op");
|
||||
if exports.insert(name, (id, type_id)).is_some() {
|
||||
return Err(LinkerError::MultipleExports(name.to_string()));
|
||||
sess.err(&format!("Multiple exports found for {:?}", name));
|
||||
return Err(ErrorReported);
|
||||
}
|
||||
}
|
||||
// Then, collect all the imports, and create the rewrite rules.
|
||||
@ -42,13 +47,14 @@ fn find_import_export_pairs_and_killed_params(
|
||||
};
|
||||
let (export_id, export_type) = match exports.get(name) {
|
||||
None => {
|
||||
return Err(LinkerError::UnresolvedSymbol(name.to_string()));
|
||||
sess.err(&format!("Unresolved symbol {:?}", name));
|
||||
return Err(ErrorReported);
|
||||
}
|
||||
Some(&x) => x,
|
||||
};
|
||||
let import_type = *type_map.get(&import_id).expect("Unexpected op");
|
||||
// Make sure the import/export pair has the same type.
|
||||
check_tys_equal(name, import_type, export_type)?;
|
||||
check_tys_equal(sess, name, import_type, export_type)?;
|
||||
rewrite_rules.insert(import_id, export_id);
|
||||
if let Some(params) = fn_parameters.get(&import_id) {
|
||||
for ¶m in params {
|
||||
@ -97,13 +103,12 @@ fn fn_parameters(module: &Module) -> HashMap<Word, Vec<Word>> {
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn check_tys_equal(name: &str, import_type: Word, export_type: Word) -> Result<()> {
|
||||
fn check_tys_equal(sess: &Session, name: &str, import_type: Word, export_type: Word) -> Result<()> {
|
||||
if import_type == export_type {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(LinkerError::TypeMismatch {
|
||||
name: name.to_string(),
|
||||
})
|
||||
sess.err(&format!("Types mismatch for {:?}", name));
|
||||
Err(ErrorReported)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,31 +14,11 @@ mod zombies;
|
||||
use rspirv::binary::Consumer;
|
||||
use rspirv::dr::{Block, Instruction, Loader, Module, ModuleHeader};
|
||||
use rspirv::spirv::{Op, Word};
|
||||
use rustc_errors::ErrorReported;
|
||||
use rustc_session::Session;
|
||||
use std::collections::HashMap;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug, PartialEq)]
|
||||
pub enum LinkerError {
|
||||
#[error("Unresolved symbol {:?}", .0)]
|
||||
UnresolvedSymbol(String),
|
||||
#[error("Multiple exports found for {:?}", .0)]
|
||||
MultipleExports(String),
|
||||
#[error("Types mismatch for {:?}", .name)]
|
||||
TypeMismatch { name: String },
|
||||
#[error("spirv-tools error {:#}", .0)]
|
||||
#[cfg(test)]
|
||||
SpirvTool(spirv_tools::Error),
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
impl From<spirv_tools::Error> for LinkerError {
|
||||
fn from(err: spirv_tools::Error) -> Self {
|
||||
Self::SpirvTool(err)
|
||||
}
|
||||
}
|
||||
|
||||
pub type Result<T> = std::result::Result<T, LinkerError>;
|
||||
pub type Result<T> = std::result::Result<T, ErrorReported>;
|
||||
|
||||
pub struct Options {
|
||||
pub compact_ids: bool,
|
||||
@ -88,10 +68,9 @@ fn apply_rewrite_rules(rewrite_rules: &HashMap<Word, Word>, 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: Option<&Session>, inputs: &mut [&mut Module], opts: &Options) -> Result<Module> {
|
||||
let timer = |n| sess.map(|s| s.timer(n));
|
||||
pub fn link(sess: &Session, mut inputs: Vec<Module>, opts: &Options) -> Result<Module> {
|
||||
let mut output = {
|
||||
let _timer = timer("link_merge");
|
||||
let _timer = sess.timer("link_merge");
|
||||
// shift all the ids
|
||||
let mut bound = inputs[0].header.as_ref().unwrap().bound - 1;
|
||||
let version = inputs[0].header.as_ref().unwrap().version();
|
||||
@ -101,20 +80,17 @@ pub fn link(sess: Option<&Session>, inputs: &mut [&mut Module], opts: &Options)
|
||||
bound += module.header.as_ref().unwrap().bound - 1;
|
||||
let this_version = module.header.as_ref().unwrap().version();
|
||||
if version != this_version {
|
||||
match sess {
|
||||
Some(sess) => sess.fatal(&format!(
|
||||
sess.fatal(&format!(
|
||||
"cannot link two modules with different SPIR-V versions: v{}.{} and v{}.{}",
|
||||
version.0, version.1, this_version.0, this_version.1
|
||||
)),
|
||||
None => panic!("spir-v version mismatch: {:?} {:?}", version, this_version),
|
||||
}
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
// merge the binaries
|
||||
let mut loader = Loader::new();
|
||||
|
||||
for module in inputs.iter() {
|
||||
for module in inputs {
|
||||
module.all_inst_iter().for_each(|inst| {
|
||||
loader.consume_instruction(inst.clone());
|
||||
});
|
||||
@ -129,7 +105,7 @@ pub fn link(sess: Option<&Session>, inputs: &mut [&mut Module], opts: &Options)
|
||||
|
||||
// remove duplicates (https://github.com/KhronosGroup/SPIRV-Tools/blob/e7866de4b1dc2a7e8672867caeb0bdca49f458d3/source/opt/remove_duplicates_pass.cpp)
|
||||
{
|
||||
let _timer = timer("link_remove_duplicates");
|
||||
let _timer = sess.timer("link_remove_duplicates");
|
||||
duplicates::remove_duplicate_extensions(&mut output);
|
||||
duplicates::remove_duplicate_capablities(&mut output);
|
||||
duplicates::remove_duplicate_ext_inst_imports(&mut output);
|
||||
@ -139,32 +115,32 @@ pub fn link(sess: Option<&Session>, inputs: &mut [&mut Module], opts: &Options)
|
||||
|
||||
// find import / export pairs
|
||||
{
|
||||
let _timer = timer("link_find_pairs");
|
||||
import_export_link::run(&mut output)?;
|
||||
let _timer = sess.timer("link_find_pairs");
|
||||
import_export_link::run(sess, &mut output)?;
|
||||
}
|
||||
|
||||
{
|
||||
let _timer = timer("link_remove_zombies");
|
||||
let _timer = sess.timer("link_remove_zombies");
|
||||
zombies::remove_zombies(sess, &mut output);
|
||||
}
|
||||
|
||||
if opts.inline {
|
||||
let _timer = timer("link_inline");
|
||||
let _timer = sess.timer("link_inline");
|
||||
inline::inline(&mut output);
|
||||
}
|
||||
|
||||
if opts.dce {
|
||||
let _timer = timer("link_dce");
|
||||
let _timer = sess.timer("link_dce");
|
||||
dce::dce(&mut output);
|
||||
}
|
||||
|
||||
if opts.structurize {
|
||||
let _timer = timer("link_structurize");
|
||||
let _timer = sess.timer("link_structurize");
|
||||
structurizer::structurize(sess, &mut output);
|
||||
}
|
||||
|
||||
{
|
||||
let _timer = timer("link_block_ordering_pass_and_mem2reg");
|
||||
let _timer = sess.timer("link_block_ordering_pass_and_mem2reg");
|
||||
let mut pointer_to_pointee = HashMap::new();
|
||||
let mut constants = HashMap::new();
|
||||
if opts.mem2reg {
|
||||
@ -205,18 +181,18 @@ pub fn link(sess: Option<&Session>, inputs: &mut [&mut Module], opts: &Options)
|
||||
}
|
||||
}
|
||||
{
|
||||
let _timer = timer("link_sort_globals");
|
||||
let _timer = sess.timer("link_sort_globals");
|
||||
simple_passes::sort_globals(&mut output);
|
||||
}
|
||||
|
||||
{
|
||||
let _timer = timer("link_remove_extra_capabilities");
|
||||
let _timer = sess.timer("link_remove_extra_capabilities");
|
||||
capability_computation::remove_extra_capabilities(&mut output);
|
||||
capability_computation::remove_extra_extensions(&mut output);
|
||||
}
|
||||
|
||||
if opts.compact_ids {
|
||||
let _timer = timer("link_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(&mut output);
|
||||
};
|
||||
|
@ -132,15 +132,7 @@ impl ControlFlowInfo {
|
||||
}
|
||||
}
|
||||
|
||||
fn emit_compiler_error(sess: Option<&Session>, msg: &'static str) -> ! {
|
||||
if let Some(sess) = sess {
|
||||
sess.fatal(msg);
|
||||
} else {
|
||||
panic!(msg);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn structurize(sess: Option<&Session>, module: &mut Module) {
|
||||
pub fn structurize(sess: &Session, module: &mut Module) {
|
||||
let mut debug_names = Vec::new();
|
||||
|
||||
for func in &mut module.functions {
|
||||
@ -508,7 +500,7 @@ fn make_unreachable_block(header: &mut ModuleHeader, blocks: &mut Vec<Block>) ->
|
||||
}
|
||||
|
||||
pub fn insert_selection_merge_on_conditional_branch(
|
||||
sess: Option<&Session>,
|
||||
sess: &Session,
|
||||
header: &mut ModuleHeader,
|
||||
blocks: &mut Vec<Block>,
|
||||
cf_info: &mut ControlFlowInfo,
|
||||
@ -580,19 +572,13 @@ pub fn insert_selection_merge_on_conditional_branch(
|
||||
blocks[a_first_idx].label_id().unwrap()
|
||||
} else if branch_a_returns {
|
||||
// (partially unreachable) merge block becomes end/start of b.
|
||||
emit_compiler_error(
|
||||
sess,
|
||||
"UNIMPLEMENTED, A partially unreachable case was detected on a.",
|
||||
);
|
||||
sess.fatal("UNIMPLEMENTED, A partially unreachable case was detected on a.");
|
||||
} else if branch_b_returns {
|
||||
// (partially unreachable) merge block becomes end/start of a.
|
||||
emit_compiler_error(
|
||||
sess,
|
||||
"UNIMPLEMENTED, A partially unreachable case was detected on b.",
|
||||
);
|
||||
sess.fatal("UNIMPLEMENTED, A partially unreachable case was detected on b.");
|
||||
} else {
|
||||
// In theory this should never happen.
|
||||
emit_compiler_error(sess, "UNEXPECTED, Unknown exit detected.");
|
||||
sess.fatal("UNEXPECTED, Unknown exit detected.");
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1,5 +1,13 @@
|
||||
use super::{link, LinkerError, Options, Result};
|
||||
use super::{link, Options};
|
||||
use pipe::pipe;
|
||||
use rspirv::dr::{Loader, Module};
|
||||
use rustc_driver::handle_options;
|
||||
use rustc_errors::registry::Registry;
|
||||
use rustc_session::config::build_session_options;
|
||||
use rustc_session::config::Input;
|
||||
use rustc_session::DiagnosticOutput;
|
||||
use std::io::Read;
|
||||
use std::path::PathBuf;
|
||||
|
||||
// https://github.com/colin-kiegel/rust-pretty-assertions/issues/24
|
||||
#[derive(PartialEq, Eq)]
|
||||
@ -12,15 +20,17 @@ impl<'a> std::fmt::Debug for PrettyString<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn assemble_spirv(spirv: &str) -> Result<Vec<u8>> {
|
||||
fn assemble_spirv(spirv: &str) -> Vec<u8> {
|
||||
use spirv_tools::assembler::{self, Assembler};
|
||||
|
||||
let assembler = assembler::create(None);
|
||||
|
||||
let spv_binary = assembler.assemble(spirv, assembler::AssemblerOptions::default())?;
|
||||
let spv_binary = assembler
|
||||
.assemble(spirv, assembler::AssemblerOptions::default())
|
||||
.expect("Failed to assemble test spir-v");
|
||||
|
||||
let contents: &[u8] = spv_binary.as_ref();
|
||||
Ok(contents.to_vec())
|
||||
contents.to_vec()
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
@ -40,13 +50,44 @@ fn load(bytes: &[u8]) -> Module {
|
||||
loader.module()
|
||||
}
|
||||
|
||||
fn assemble_and_link(binaries: &[&[u8]]) -> super::Result<Module> {
|
||||
let mut modules = binaries.iter().cloned().map(load).collect::<Vec<_>>();
|
||||
let mut modules = modules.iter_mut().collect::<Vec<_>>();
|
||||
fn assemble_and_link(binaries: &[&[u8]]) -> Result<Module, String> {
|
||||
let modules = binaries.iter().cloned().map(load).collect::<Vec<_>>();
|
||||
|
||||
link(
|
||||
None,
|
||||
&mut modules,
|
||||
// need pipe here because Config takes ownership of the writer, and the writer must be 'static.
|
||||
let (mut read_diags, write_diags) = pipe();
|
||||
let thread = std::thread::spawn(move || {
|
||||
// must spawn thread because pipe crate is synchronous
|
||||
let mut diags = String::new();
|
||||
read_diags.read_to_string(&mut diags).unwrap();
|
||||
let suffix = "\n\nerror: aborting due to previous error\n\n";
|
||||
if diags.ends_with(suffix) {
|
||||
diags.truncate(diags.len() - suffix.len());
|
||||
}
|
||||
diags
|
||||
});
|
||||
let matches = handle_options(&["".to_string(), "x.rs".to_string()]).unwrap();
|
||||
let sopts = build_session_options(&matches);
|
||||
let config = rustc_interface::Config {
|
||||
opts: sopts,
|
||||
crate_cfg: Default::default(),
|
||||
input: Input::File(PathBuf::new()),
|
||||
input_path: None,
|
||||
output_file: None,
|
||||
output_dir: None,
|
||||
file_loader: None,
|
||||
diagnostic_output: DiagnosticOutput::Raw(Box::new(write_diags)),
|
||||
stderr: None,
|
||||
crate_name: None,
|
||||
lint_caps: Default::default(),
|
||||
register_lints: None,
|
||||
override_queries: None,
|
||||
make_codegen_backend: None,
|
||||
registry: Registry::new(&[]),
|
||||
};
|
||||
let res = rustc_interface::interface::run_compiler(config, |compiler| {
|
||||
let res = link(
|
||||
compiler.session(),
|
||||
modules,
|
||||
&Options {
|
||||
compact_ids: true,
|
||||
dce: false,
|
||||
@ -54,7 +95,11 @@ fn assemble_and_link(binaries: &[&[u8]]) -> super::Result<Module> {
|
||||
mem2reg: false,
|
||||
structurize: false,
|
||||
},
|
||||
)
|
||||
);
|
||||
assert_eq!(compiler.session().has_errors(), res.is_err());
|
||||
res
|
||||
});
|
||||
res.map_err(|_| thread.join().unwrap())
|
||||
}
|
||||
|
||||
fn without_header_eq(mut result: Module, expected: &str) {
|
||||
@ -91,14 +136,14 @@ fn without_header_eq(mut result: Module, expected: &str) {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn standard() -> Result<()> {
|
||||
fn standard() {
|
||||
let a = assemble_spirv(
|
||||
r#"OpCapability Linkage
|
||||
OpDecorate %1 LinkageAttributes "foo" Import
|
||||
%2 = OpTypeFloat 32
|
||||
%1 = OpVariable %2 Uniform
|
||||
%3 = OpVariable %2 Input"#,
|
||||
)?;
|
||||
);
|
||||
|
||||
let b = assemble_spirv(
|
||||
r#"OpCapability Linkage
|
||||
@ -107,38 +152,19 @@ fn standard() -> Result<()> {
|
||||
%3 = OpConstant %2 42
|
||||
%1 = OpVariable %2 Uniform %3
|
||||
"#,
|
||||
)?;
|
||||
);
|
||||
|
||||
let result = assemble_and_link(&[&a, &b])?;
|
||||
let result = assemble_and_link(&[&a, &b]).unwrap();
|
||||
let expect = r#"%1 = OpTypeFloat 32
|
||||
%2 = OpVariable %1 Input
|
||||
%3 = OpConstant %1 42.0
|
||||
%4 = OpVariable %1 Uniform %3"#;
|
||||
|
||||
without_header_eq(result, expect);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn not_a_lib_extra_exports() -> Result<()> {
|
||||
let a = assemble_spirv(
|
||||
r#"OpCapability Linkage
|
||||
OpDecorate %1 LinkageAttributes "foo" Export
|
||||
%2 = OpTypeFloat 32
|
||||
%1 = OpVariable %2 Uniform"#,
|
||||
)?;
|
||||
|
||||
let result = assemble_and_link(&[&a])?;
|
||||
let expect = r#"%1 = OpTypeFloat 32
|
||||
%2 = OpVariable %1 Uniform"#;
|
||||
without_header_eq(result, expect);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// TODO: Lib mode is not supported yet. Double-TODO: Will it *ever* be supported? (probably not)
|
||||
/*
|
||||
#[test]
|
||||
fn lib_extra_exports() -> Result<()> {
|
||||
fn not_a_lib_extra_exports() {
|
||||
let a = assemble_spirv(
|
||||
r#"OpCapability Linkage
|
||||
OpDecorate %1 LinkageAttributes "foo" Export
|
||||
@ -146,46 +172,40 @@ fn lib_extra_exports() -> Result<()> {
|
||||
%1 = OpVariable %2 Uniform"#,
|
||||
);
|
||||
|
||||
let result = assemble_and_link(&[&a])?;
|
||||
|
||||
let expect = r#"OpDecorate %1 LinkageAttributes "foo" Export
|
||||
%2 = OpTypeFloat 32
|
||||
%1 = OpVariable %2 Uniform"#;
|
||||
let result = assemble_and_link(&[&a]).unwrap();
|
||||
let expect = r#"%1 = OpTypeFloat 32
|
||||
%2 = OpVariable %1 Uniform"#;
|
||||
without_header_eq(result, expect);
|
||||
Ok(())
|
||||
}
|
||||
*/
|
||||
|
||||
#[test]
|
||||
fn unresolved_symbol() -> Result<()> {
|
||||
fn unresolved_symbol() {
|
||||
let a = assemble_spirv(
|
||||
r#"OpCapability Linkage
|
||||
OpDecorate %1 LinkageAttributes "foo" Import
|
||||
%2 = OpTypeFloat 32
|
||||
%1 = OpVariable %2 Uniform"#,
|
||||
)?;
|
||||
);
|
||||
|
||||
let b = assemble_spirv("OpCapability Linkage")?;
|
||||
let b = assemble_spirv("OpCapability Linkage");
|
||||
|
||||
let result = assemble_and_link(&[&a, &b]);
|
||||
|
||||
assert_eq!(
|
||||
result.err(),
|
||||
Some(LinkerError::UnresolvedSymbol("foo".to_string()))
|
||||
result.err().as_deref(),
|
||||
Some("error: Unresolved symbol \"foo\"")
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn type_mismatch() -> Result<()> {
|
||||
fn type_mismatch() {
|
||||
let a = assemble_spirv(
|
||||
r#"OpCapability Linkage
|
||||
OpDecorate %1 LinkageAttributes "foo" Import
|
||||
%2 = OpTypeFloat 32
|
||||
%1 = OpVariable %2 Uniform
|
||||
%3 = OpVariable %2 Input"#,
|
||||
)?;
|
||||
);
|
||||
|
||||
let b = assemble_spirv(
|
||||
r#"OpCapability Linkage
|
||||
@ -194,27 +214,24 @@ fn type_mismatch() -> Result<()> {
|
||||
%3 = OpConstant %2 42
|
||||
%1 = OpVariable %2 Uniform %3
|
||||
"#,
|
||||
)?;
|
||||
);
|
||||
|
||||
let result = assemble_and_link(&[&a, &b]);
|
||||
assert_eq!(
|
||||
result.err(),
|
||||
Some(LinkerError::TypeMismatch {
|
||||
name: "foo".to_string(),
|
||||
})
|
||||
result.err().as_deref(),
|
||||
Some("error: Types mismatch for \"foo\"")
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiple_definitions() -> Result<()> {
|
||||
fn multiple_definitions() {
|
||||
let a = assemble_spirv(
|
||||
r#"OpCapability Linkage
|
||||
OpDecorate %1 LinkageAttributes "foo" Import
|
||||
%2 = OpTypeFloat 32
|
||||
%1 = OpVariable %2 Uniform
|
||||
%3 = OpVariable %2 Input"#,
|
||||
)?;
|
||||
);
|
||||
|
||||
let b = assemble_spirv(
|
||||
r#"OpCapability Linkage
|
||||
@ -223,7 +240,7 @@ fn multiple_definitions() -> Result<()> {
|
||||
%2 = OpTypeFloat 32
|
||||
%3 = OpConstant %2 42
|
||||
%1 = OpVariable %2 Uniform %3"#,
|
||||
)?;
|
||||
);
|
||||
|
||||
let c = assemble_spirv(
|
||||
r#"OpCapability Linkage
|
||||
@ -231,25 +248,24 @@ fn multiple_definitions() -> Result<()> {
|
||||
%2 = OpTypeFloat 32
|
||||
%3 = OpConstant %2 -1
|
||||
%1 = OpVariable %2 Uniform %3"#,
|
||||
)?;
|
||||
);
|
||||
|
||||
let result = assemble_and_link(&[&a, &b, &c]);
|
||||
assert_eq!(
|
||||
result.err(),
|
||||
Some(LinkerError::MultipleExports("foo".to_string()))
|
||||
result.err().as_deref(),
|
||||
Some("error: Multiple exports found for \"foo\"")
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiple_definitions_different_types() -> Result<()> {
|
||||
fn multiple_definitions_different_types() {
|
||||
let a = assemble_spirv(
|
||||
r#"OpCapability Linkage
|
||||
OpDecorate %1 LinkageAttributes "foo" Import
|
||||
%2 = OpTypeFloat 32
|
||||
%1 = OpVariable %2 Uniform
|
||||
%3 = OpVariable %2 Input"#,
|
||||
)?;
|
||||
);
|
||||
|
||||
let b = assemble_spirv(
|
||||
r#"OpCapability Linkage
|
||||
@ -258,7 +274,7 @@ fn multiple_definitions_different_types() -> Result<()> {
|
||||
%2 = OpTypeInt 32 0
|
||||
%3 = OpConstant %2 42
|
||||
%1 = OpVariable %2 Uniform %3"#,
|
||||
)?;
|
||||
);
|
||||
|
||||
let c = assemble_spirv(
|
||||
r#"OpCapability Linkage
|
||||
@ -266,19 +282,18 @@ fn multiple_definitions_different_types() -> Result<()> {
|
||||
%2 = OpTypeFloat 32
|
||||
%3 = OpConstant %2 12
|
||||
%1 = OpVariable %2 Uniform %3"#,
|
||||
)?;
|
||||
);
|
||||
|
||||
let result = assemble_and_link(&[&a, &b, &c]);
|
||||
assert_eq!(
|
||||
result.err(),
|
||||
Some(LinkerError::MultipleExports("foo".to_string()))
|
||||
result.err().as_deref(),
|
||||
Some("error: Multiple exports found for \"foo\"")
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
//jb-todo: this isn't validated yet in the linker (see ensure_matching_import_export_pairs)
|
||||
/*#[test]
|
||||
fn decoration_mismatch() -> Result<()> {
|
||||
fn decoration_mismatch() {
|
||||
let a = assemble_spirv(
|
||||
r#"OpCapability Linkage
|
||||
OpDecorate %1 LinkageAttributes "foo" Import
|
||||
@ -305,7 +320,7 @@ fn decoration_mismatch() -> Result<()> {
|
||||
}*/
|
||||
|
||||
#[test]
|
||||
fn func_ctrl() -> Result<()> {
|
||||
fn func_ctrl() {
|
||||
let a = assemble_spirv(
|
||||
r#"OpCapability Linkage
|
||||
OpDecorate %1 LinkageAttributes "foo" Import
|
||||
@ -315,7 +330,7 @@ fn func_ctrl() -> Result<()> {
|
||||
%5 = OpVariable %4 Uniform
|
||||
%1 = OpFunction %2 None %3
|
||||
OpFunctionEnd"#,
|
||||
)?;
|
||||
);
|
||||
|
||||
let b = assemble_spirv(
|
||||
r#"OpCapability Linkage
|
||||
@ -326,9 +341,9 @@ fn func_ctrl() -> Result<()> {
|
||||
%4 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd"#,
|
||||
)?;
|
||||
);
|
||||
|
||||
let result = assemble_and_link(&[&a, &b])?;
|
||||
let result = assemble_and_link(&[&a, &b]).unwrap();
|
||||
|
||||
let expect = r#"%1 = OpTypeVoid
|
||||
%2 = OpTypeFunction %1
|
||||
@ -340,11 +355,10 @@ fn func_ctrl() -> Result<()> {
|
||||
OpFunctionEnd"#;
|
||||
|
||||
without_header_eq(result, expect);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn use_exported_func_param_attr() -> Result<()> {
|
||||
fn use_exported_func_param_attr() {
|
||||
let a = assemble_spirv(
|
||||
r#"OpCapability Kernel
|
||||
OpCapability Linkage
|
||||
@ -362,7 +376,7 @@ fn use_exported_func_param_attr() -> Result<()> {
|
||||
%4 = OpFunctionParameter %6
|
||||
OpFunctionEnd
|
||||
"#,
|
||||
)?;
|
||||
);
|
||||
|
||||
let b = assemble_spirv(
|
||||
r#"OpCapability Kernel
|
||||
@ -378,9 +392,9 @@ fn use_exported_func_param_attr() -> Result<()> {
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
"#,
|
||||
)?;
|
||||
);
|
||||
|
||||
let result = assemble_and_link(&[&a, &b])?;
|
||||
let result = assemble_and_link(&[&a, &b]).unwrap();
|
||||
|
||||
let expect = r#"OpCapability Kernel
|
||||
OpDecorate %1 FuncParamAttr Zext
|
||||
@ -400,11 +414,10 @@ fn use_exported_func_param_attr() -> Result<()> {
|
||||
OpFunctionEnd"#;
|
||||
|
||||
without_header_eq(result, expect);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn names_and_decorations() -> Result<()> {
|
||||
fn names_and_decorations() {
|
||||
let a = assemble_spirv(
|
||||
r#"OpCapability Kernel
|
||||
OpCapability Linkage
|
||||
@ -426,7 +439,7 @@ fn names_and_decorations() -> Result<()> {
|
||||
%4 = OpFunctionParameter %9
|
||||
OpFunctionEnd
|
||||
"#,
|
||||
)?;
|
||||
);
|
||||
|
||||
let b = assemble_spirv(
|
||||
r#"OpCapability Kernel
|
||||
@ -445,9 +458,9 @@ fn names_and_decorations() -> Result<()> {
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
"#,
|
||||
)?;
|
||||
);
|
||||
|
||||
let result = assemble_and_link(&[&a, &b])?;
|
||||
let result = assemble_and_link(&[&a, &b]).unwrap();
|
||||
|
||||
let expect = r#"OpCapability Kernel
|
||||
OpName %1 "foo"
|
||||
@ -471,5 +484,4 @@ fn names_and_decorations() -> Result<()> {
|
||||
OpFunctionEnd"#;
|
||||
|
||||
without_header_eq(result, expect);
|
||||
Ok(())
|
||||
}
|
||||
|
@ -160,7 +160,7 @@ fn report_error_zombies(sess: &Session, module: &Module, zombie: &HashMap<Word,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remove_zombies(sess: Option<&Session>, module: &mut Module) {
|
||||
pub fn remove_zombies(sess: &Session, module: &mut Module) {
|
||||
let zombies_owned = collect_zombies(module);
|
||||
let mut zombies = zombies_owned
|
||||
.iter()
|
||||
@ -170,9 +170,7 @@ pub fn remove_zombies(sess: Option<&Session>, module: &mut Module) {
|
||||
// Note: This is O(n^2).
|
||||
while spread_zombie(module, &mut zombies) {}
|
||||
|
||||
if let Some(sess) = sess {
|
||||
report_error_zombies(sess, module, &zombies);
|
||||
}
|
||||
|
||||
if env::var("PRINT_ALL_ZOMBIE").is_ok() {
|
||||
for (&zomb, reason) in &zombies {
|
||||
|
Loading…
Reference in New Issue
Block a user