Synthesize Session for tests, instead of Option<Session> (#228)

Also remove LinkerError and use rustc error reporting directly instead
This commit is contained in:
Ashley Hauck 2020-11-11 14:22:06 +01:00 committed by GitHub
parent 47d23ba800
commit 86da42f2d7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 201 additions and 205 deletions

42
Cargo.lock generated
View File

@ -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",
]

View File

@ -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"

View File

@ -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 {

View File

@ -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")
}
};

View File

@ -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 &param 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)
}
}

View File

@ -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!(
"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),
}
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
))
}
}
// 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);
};

View File

@ -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.");
}
};

View File

@ -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,21 +50,56 @@ 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,
&Options {
compact_ids: true,
dce: false,
inline: false,
mem2reg: false,
structurize: false,
},
)
// 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,
inline: false,
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(())
}

View File

@ -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);
}
report_error_zombies(sess, module, &zombies);
if env::var("PRINT_ALL_ZOMBIE").is_ok() {
for (&zomb, reason) in &zombies {