Start work on integrating linker

This commit is contained in:
khyperia 2020-09-11 13:09:06 +02:00
parent f890ce0fcc
commit c5dfee6e1e
15 changed files with 409 additions and 54 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/target

View File

@ -49,7 +49,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39858aa5bac06462d4dd4b9164848eb81ffc4aa5c479746393598fd193afa227"
dependencies = [
"quote 1.0.7",
"syn 1.0.39",
"syn 1.0.40",
]
[[package]]
@ -72,6 +72,18 @@ version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198"
[[package]]
name = "filetime"
version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ed85775dcc68644b5c950ac06a2b23768d3bc9390464151aaf27136998dcf9e"
dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"winapi",
]
[[package]]
name = "fxhash"
version = "0.2.1"
@ -83,9 +95,9 @@ dependencies = [
[[package]]
name = "getrandom"
version = "0.1.14"
version = "0.1.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb"
checksum = "fc587bc0ec293155d5bfa6b9891ec18a1e330c234f896ea47fbada4cadbe47e6"
dependencies = [
"cfg-if",
"libc",
@ -100,9 +112,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.76"
version = "0.2.77"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "755456fae044e6fa1ebbbd1b3e902ae19e73097ed4ed87bb79934a867c007bc3"
checksum = "f2f96b10ec2560088a8e76961b00d47107b3a625fecb76dedb29ee7ccbf98235"
[[package]]
name = "memchr"
@ -157,9 +169,9 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.19"
version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04f5f085b5d71e2188cb8271e5da0161ad52c3f227a661a3c135fdf28e258b12"
checksum = "36e28516df94f3dd551a587da5357459d9b36d945a7c37c3557928c1c2ff2a2c"
dependencies = [
"unicode-xid 0.2.1",
]
@ -179,7 +191,7 @@ version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37"
dependencies = [
"proc-macro2 1.0.19",
"proc-macro2 1.0.21",
]
[[package]]
@ -259,6 +271,7 @@ dependencies = [
[[package]]
name = "rspirv"
version = "0.7.0"
source = "git+https://github.com/gfx-rs/rspirv#25737220af8703f2b48edd368e63f413f3dfd0e4"
dependencies = [
"derive_more",
"fxhash",
@ -277,6 +290,18 @@ dependencies = [
"topological-sort",
]
[[package]]
name = "rustc_codegen_spirv"
version = "0.1.0"
dependencies = [
"pretty_assertions",
"rspirv",
"rspirv-linker",
"tar",
"tempfile",
"topological-sort",
]
[[package]]
name = "rustc_version"
version = "0.2.3"
@ -304,6 +329,7 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
[[package]]
name = "spirv_headers"
version = "1.5.0"
source = "git+https://github.com/gfx-rs/rspirv#25737220af8703f2b48edd368e63f413f3dfd0e4"
dependencies = [
"bitflags",
"num-traits",
@ -322,15 +348,27 @@ dependencies = [
[[package]]
name = "syn"
version = "1.0.39"
version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "891d8d6567fe7c7f8835a3a98af4208f3846fba258c1bc3c31d6e506239f11f9"
checksum = "963f7d3cc59b59b9325165add223142bbf1df27655d07789f109896d353d8350"
dependencies = [
"proc-macro2 1.0.19",
"proc-macro2 1.0.21",
"quote 1.0.7",
"unicode-xid 0.2.1",
]
[[package]]
name = "tar"
version = "0.4.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "489997b7557e9a43e192c527face4feacc78bfbe6eed67fd55c4c9e381cba290"
dependencies = [
"filetime",
"libc",
"redox_syscall",
"xattr",
]
[[package]]
name = "tempfile"
version = "3.1.0"
@ -360,9 +398,9 @@ version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd80fc12f73063ac132ac92aceea36734f04a1d93c1240c6944e23a3b8841793"
dependencies = [
"proc-macro2 1.0.19",
"proc-macro2 1.0.21",
"quote 1.0.7",
"syn 1.0.39",
"syn 1.0.40",
]
[[package]]
@ -419,3 +457,12 @@ name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "xattr"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "244c3741f4240ef46274860397c7c74e50eb23624996930e484c16679633a54c"
dependencies = [
"libc",
]

5
Cargo.toml Normal file
View File

@ -0,0 +1,5 @@
[workspace]
members = [
"rustc_codegen_spirv",
"rspirv-linker",
]

View File

@ -7,7 +7,7 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
rspirv = { path = "C:/Users/Jasper/traverse/rspirv/rspirv/"}
rspirv = { git = "https://github.com/gfx-rs/rspirv" }
topological-sort = "0.1"
thiserror = "1.0.20"

View File

@ -1,3 +1,4 @@
#[cfg(test)]
mod test;
use rspirv::binary::Consumer;
@ -410,12 +411,12 @@ fn import_kill_annotations_and_debug(module: &mut rspirv::dr::Module, info: &Lin
}
}
struct Options {
pub struct Options {
/// `true` if we're creating a library
lib: bool,
pub lib: bool,
/// `true` if partial linking is allowed
partial: bool,
pub partial: bool,
}
impl Default for Options {
@ -769,7 +770,7 @@ fn trans_aggregate_type(
})
}
fn link(inputs: &mut [&mut rspirv::dr::Module], opts: &Options) -> Result<rspirv::dr::Module> {
pub fn link(inputs: &mut [&mut rspirv::dr::Module], opts: &Options) -> Result<rspirv::dr::Module> {
// shift all the ids
let mut bound = inputs[0].header.as_ref().unwrap().bound - 1;

View File

@ -1,3 +0,0 @@
/target
Cargo.lock
*.spv

View File

@ -14,6 +14,8 @@ crate-type = ["dylib"]
[dependencies]
rspirv = { git = "https://github.com/gfx-rs/rspirv" }
rspirv-linker = { path = "../rspirv-linker" }
tar = "0.4"
topological-sort = "0.1"
[dev-dependencies]

View File

@ -6,7 +6,7 @@ set -e
# build rustc_codegen_spirv
cargo build
export RUSTFLAGS=-Zcodegen-backend=$PWD/target/debug/librustc_codegen_spirv.so
export RUSTFLAGS=-Zcodegen-backend=$PWD/../target/debug/librustc_codegen_spirv.so
pushd build_libcore_test
# Use wasm32 because it's a relatively simple platform - if the x86 libcore is used, there's all sorts of "feature sse2

View File

@ -1 +1,2 @@
/target/
/Cargo.lock

View File

@ -4,4 +4,9 @@ version = "0.1.0"
authors = ["Ashley Hauck <ashley.hauck@embark-studios.com>"]
edition = "2018"
[lib]
crate-type = ["cdylib"]
[dependencies]
[workspace]

View File

@ -73,15 +73,9 @@ impl BuilderSpirv {
let disas = module.disassemble();
println!("{}", disas);
let spirv_module = module.assemble();
let spirv_module_u8: &[u8] = unsafe {
std::slice::from_raw_parts(
spirv_module.as_ptr() as *const u8,
spirv_module.len() * std::mem::size_of::<u32>(),
)
};
File::create(path)
.unwrap()
.write_all(spirv_module_u8)
.write_all(crate::slice_u32_to_u8(&spirv_module))
.unwrap();
}

View File

@ -724,15 +724,22 @@ impl<'tcx> PreDefineMethods<'tcx> for CodegenCx<'tcx> {
fn predefine_fn(
&self,
instance: Instance<'tcx>,
_linkage: Linkage,
linkage: Linkage,
visibility: Visibility,
symbol_name: &str,
) {
let fn_abi = FnAbi::of_instance(self, instance, &[]);
let human_name = format!("{}", instance);
if symbol_name == "_ZN51_$LT$i32$u20$as$u20$compiler_builtins..int..Int$GT$12extract_sign17h33d8e137c5cab7faE" {
println!("{} = {:?} {:?}", human_name, linkage, visibility);
}
let linkage = match visibility {
Visibility::Default | Visibility::Protected => Some(LinkageType::Export),
Visibility::Hidden => None,
Visibility::Hidden => match linkage {
Linkage::External | Linkage::AvailableExternally => Some(LinkageType::Export),
// TODO: Figure out linkage types
_ => None,
},
};
let declared = declare_fn(self, symbol_name, Some(&human_name), linkage, &fn_abi);
self.instances.borrow_mut().insert(instance, declared);

View File

@ -2,14 +2,17 @@
#![feature(once_cell)]
extern crate rustc_ast;
extern crate rustc_attr;
extern crate rustc_codegen_ssa;
extern crate rustc_data_structures;
extern crate rustc_driver;
extern crate rustc_errors;
extern crate rustc_hir;
extern crate rustc_incremental;
extern crate rustc_interface;
extern crate rustc_middle;
extern crate rustc_mir;
extern crate rustc_serialize;
extern crate rustc_session;
extern crate rustc_span;
extern crate rustc_symbol_mangling;
@ -33,7 +36,7 @@ use codegen_cx::CodegenCx;
use rspirv::binary::Assemble;
use rustc_ast::expand::allocator::AllocatorKind;
use rustc_codegen_ssa::back::lto::{LtoModuleCodegen, SerializedModule, ThinModule};
use rustc_codegen_ssa::back::write::{CodegenContext, FatLTOInput, ModuleConfig};
use rustc_codegen_ssa::back::write::{CodegenContext, FatLTOInput, ModuleConfig, OngoingCodegen};
use rustc_codegen_ssa::base::maybe_create_entry_wrapper;
use rustc_codegen_ssa::mono_item::MonoItemExt;
use rustc_codegen_ssa::traits::{CodegenBackend, ExtraBackendMethods, WriteBackendMethods};
@ -48,7 +51,8 @@ use rustc_middle::mir::mono::MonoItem;
use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::{Instance, InstanceDef, TyCtxt};
use rustc_session::config::{OptLevel, OutputFilenames, OutputType};
use rustc_serialize::json;
use rustc_session::config::{self, OptLevel, OutputFilenames, OutputType};
use rustc_session::Session;
use rustc_span::Symbol;
use rustc_target::spec::Target;
@ -94,9 +98,9 @@ impl MetadataLoader for NoLlvmMetadataLoader {
}
#[derive(Clone)]
struct SsaBackend;
struct SpirvCodegenBackend;
impl CodegenBackend for SsaBackend {
impl CodegenBackend for SpirvCodegenBackend {
fn metadata_loader(&self) -> Box<MetadataLoaderDyn> {
Box::new(NoLlvmMetadataLoader)
}
@ -125,7 +129,7 @@ impl CodegenBackend for SsaBackend {
need_metadata_module: bool,
) -> Box<dyn Any> {
Box::new(rustc_codegen_ssa::base::codegen_crate(
SsaBackend,
SpirvCodegenBackend,
tcx,
metadata,
need_metadata_module,
@ -136,19 +140,19 @@ impl CodegenBackend for SsaBackend {
&self,
ongoing_codegen: Box<dyn Any>,
sess: &Session,
_dep_graph: &DepGraph,
dep_graph: &DepGraph,
) -> Result<Box<dyn Any>, ErrorReported> {
let (codegen_results, _work_products) = ongoing_codegen
.downcast::<rustc_codegen_ssa::back::write::OngoingCodegen<SsaBackend>>()
.expect("Expected SpirvCodegenBackend's OngoingCodegen, found Box<Any>")
let (codegen_results, work_products) = ongoing_codegen
.downcast::<OngoingCodegen<SpirvCodegenBackend>>()
.expect("Expected OngoingCodegen, found Box<Any>")
.join(sess);
if sess.opts.debugging_opts.incremental_info {
rustc_codegen_ssa::back::write::dump_incremental_data(&codegen_results);
}
// sess.time("serialize_work_products", move || {
// rustc_incremental::save_work_product_index(sess, &dep_graph, work_products)
// });
sess.time("serialize_work_products", move || {
rustc_incremental::save_work_product_index(sess, &dep_graph, work_products)
});
sess.compile_status()?;
@ -157,15 +161,38 @@ impl CodegenBackend for SsaBackend {
fn link(
&self,
_sess: &Session,
sess: &Session,
codegen_results: Box<dyn Any>,
_outputs: &OutputFilenames,
outputs: &OutputFilenames,
) -> Result<(), ErrorReported> {
let _codegen_results = codegen_results
let codegen_results = codegen_results
.downcast::<CodegenResults>()
.expect("Expected CodegenResults, found Box<Any>");
// TODO: see rustc_codegen_llvm for example of implementation
if sess.opts.debugging_opts.no_link {
// FIXME: use a binary format to encode the `.rlink` file
let rlink_data = json::encode(&codegen_results).map_err(|err| {
sess.fatal(&format!("failed to encode rlink: {}", err));
})?;
let rlink_file = outputs.with_extension(config::RLINK_EXT);
std::fs::write(&rlink_file, rlink_data).map_err(|err| {
sess.fatal(&format!(
"failed to write file {}: {}",
rlink_file.display(),
err
));
})?;
return Ok(());
}
link::link(
sess,
&codegen_results,
outputs,
&codegen_results.crate_name.as_str(),
);
rustc_incremental::finalize_session_directory(sess, codegen_results.crate_hash);
Ok(())
}
@ -190,7 +217,7 @@ fn slice_u8_to_u32(slice: &[u8]) -> &[u32] {
}
}
impl WriteBackendMethods for SsaBackend {
impl WriteBackendMethods for SpirvCodegenBackend {
type Module = Vec<u32>;
type TargetMachine = ();
type ModuleBuffer = SpirvModuleBuffer;
@ -302,7 +329,7 @@ impl WriteBackendMethods for SsaBackend {
}
}
impl ExtraBackendMethods for SsaBackend {
impl ExtraBackendMethods for SpirvCodegenBackend {
fn new_metadata(&self, _: TyCtxt<'_>, _: &str) -> Self::Module {
todo!()
}
@ -416,7 +443,7 @@ impl Drop for DumpModuleOnPanic<'_, '_, '_> {
/// This is the entrypoint for a hot plugged rustc_codegen_spirv
#[no_mangle]
pub fn __rustc_codegen_backend() -> Box<dyn CodegenBackend> {
Box::new(SsaBackend)
Box::new(SpirvCodegenBackend)
}
// https://github.com/bjorn3/rustc_codegen_cranelift/blob/1b8df386aa72bc3dacb803f7d4deb4eadd63b56f/src/base.rs

View File

@ -1,18 +1,286 @@
use crate::things::{SpirvModuleBuffer, SpirvThinBuffer};
use crate::SsaBackend;
use crate::SpirvCodegenBackend;
use rustc_codegen_ssa::back::lto::{LtoModuleCodegen, SerializedModule, ThinModule, ThinShared};
use rustc_codegen_ssa::back::write::CodegenContext;
use rustc_codegen_ssa::CodegenResults;
use rustc_errors::FatalError;
use rustc_middle::dep_graph::WorkProduct;
use rustc_session::config::Lto;
use rustc_middle::middle::cstore::NativeLib;
use rustc_middle::middle::dependency_format::Linkage;
use rustc_session::config::{CrateType, Lto, OutputFilenames, OutputType};
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::ffi::CString;
use std::fs::File;
use std::io::Read;
use std::io::Write;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use tar::{Archive, Builder};
pub fn link<'a>(
sess: &'a Session,
codegen_results: &CodegenResults,
outputs: &OutputFilenames,
crate_name: &str,
) {
let output_metadata = sess.opts.output_types.contains_key(&OutputType::Metadata);
for &crate_type in sess.crate_types().iter() {
if (sess.opts.debugging_opts.no_codegen || !sess.opts.output_types.should_codegen())
&& !output_metadata
&& crate_type == CrateType::Executable
{
continue;
}
if invalid_output_for_target(sess, crate_type) {
panic!(
"invalid output type `{:?}` for target os `{}`",
crate_type, sess.opts.target_triple
);
}
for obj in codegen_results
.modules
.iter()
.filter_map(|m| m.object.as_ref())
{
check_file_is_writeable(obj, sess);
}
if outputs.outputs.should_codegen() {
let out_filename = out_filename(sess, crate_type, outputs, crate_name);
match crate_type {
CrateType::Rlib => {
link_rlib(codegen_results, &out_filename);
}
CrateType::Executable | CrateType::Cdylib => {
link_exe(sess, crate_type, &out_filename, codegen_results)
}
other => panic!("CrateType {:?} not supported yet", other),
}
}
}
}
fn link_rlib(codegen_results: &CodegenResults, out_filename: &Path) {
let mut file_list = Vec::<&Path>::new();
for obj in codegen_results
.modules
.iter()
.filter_map(|m| m.object.as_ref())
{
file_list.push(obj);
}
for lib in codegen_results.crate_info.used_libraries.iter() {
match lib.kind {
NativeLibKind::StaticBundle => {}
NativeLibKind::StaticNoBundle
| NativeLibKind::Dylib
| NativeLibKind::Framework
| NativeLibKind::RawDylib
| NativeLibKind::Unspecified => continue,
}
if let Some(name) = lib.name {
panic!("Adding native library to rlib not supported yet: {}", name);
}
}
// let metadata = &codegen_results.metadata.raw_data;
// file_list.push(metadata);
create_archive(&file_list, out_filename);
}
fn link_exe(
sess: &Session,
crate_type: CrateType,
out_filename: &Path,
codegen_results: &CodegenResults,
) {
let mut objects = Vec::new();
let mut rlibs = Vec::new();
for obj in codegen_results
.modules
.iter()
.filter_map(|m| m.object.as_ref())
{
objects.push(obj.clone());
}
link_local_crate_native_libs_and_dependent_crate_libs(
&mut rlibs,
sess,
crate_type,
codegen_results,
);
do_link(&objects, &rlibs, out_filename);
}
fn link_local_crate_native_libs_and_dependent_crate_libs<'a>(
rlibs: &mut Vec<PathBuf>,
sess: &'a Session,
crate_type: CrateType,
codegen_results: &CodegenResults,
) {
if sess.opts.debugging_opts.link_native_libraries {
add_local_native_libraries(sess, codegen_results);
}
add_upstream_rust_crates(rlibs, codegen_results, crate_type);
if sess.opts.debugging_opts.link_native_libraries {
add_upstream_native_libraries(sess, codegen_results, crate_type);
}
}
fn add_local_native_libraries(sess: &Session, codegen_results: &CodegenResults) {
let relevant_libs = codegen_results
.crate_info
.used_libraries
.iter()
.filter(|l| relevant_lib(sess, l));
assert_eq!(relevant_libs.count(), 0);
}
fn add_upstream_rust_crates(
rlibs: &mut Vec<PathBuf>,
codegen_results: &CodegenResults,
crate_type: CrateType,
) {
let (_, data) = codegen_results
.crate_info
.dependency_formats
.iter()
.find(|(ty, _)| *ty == crate_type)
.expect("failed to find crate type in dependency format list");
let deps = &codegen_results.crate_info.used_crates_dynamic;
for &(cnum, _) in deps.iter() {
let src = &codegen_results.crate_info.used_crate_source[&cnum];
match data[cnum.as_usize() - 1] {
Linkage::NotLinked | Linkage::IncludedFromDylib => {}
Linkage::Static => rlibs.push(src.rlib.as_ref().unwrap().0.clone()),
//Linkage::Dynamic => rlibs.push(src.dylib.as_ref().unwrap().0.clone()),
Linkage::Dynamic => panic!("TODO: Linkage::Dynamic not supported yet"),
}
}
}
fn add_upstream_native_libraries(
sess: &Session,
codegen_results: &CodegenResults,
crate_type: CrateType,
) {
let (_, data) = codegen_results
.crate_info
.dependency_formats
.iter()
.find(|(ty, _)| *ty == crate_type)
.expect("failed to find crate type in dependency format list");
let crates = &codegen_results.crate_info.used_crates_static;
for &(cnum, _) in crates {
for lib in codegen_results.crate_info.native_libraries[&cnum].iter() {
let name = match lib.name {
Some(l) => l,
None => continue,
};
if !relevant_lib(sess, &lib) {
continue;
}
match lib.kind {
NativeLibKind::Dylib | NativeLibKind::Unspecified => {
panic!("TODO: dylib nativelibkind not supported yet: {}", name)
}
NativeLibKind::Framework => {
panic!("TODO: framework nativelibkind not supported yet: {}", name)
}
NativeLibKind::StaticNoBundle => {
if data[cnum.as_usize() - 1] == Linkage::Static {
panic!(
"TODO: staticnobundle nativelibkind not supported yet: {}",
name
);
}
}
NativeLibKind::StaticBundle => {}
NativeLibKind::RawDylib => {
panic!("raw_dylib feature not yet implemented: {}", name);
}
}
}
}
}
fn relevant_lib(sess: &Session, lib: &NativeLib) -> bool {
match lib.cfg {
Some(ref cfg) => rustc_attr::cfg_matches(cfg, &sess.parse_sess, None),
None => true,
}
}
fn create_archive(files: &[&Path], out_filename: &Path) {
let file = File::create(out_filename).unwrap();
let mut builder = Builder::new(file);
let mut filenames = HashSet::new();
for file in files {
assert!(
filenames.insert(file.file_name().unwrap()),
"Duplicate filename in archive: {:?}",
file.file_name().unwrap()
);
builder
.append_path_with_name(file, file.file_name().unwrap())
.unwrap();
}
builder.into_inner().unwrap();
}
fn do_link(objects: &[PathBuf], rlibs: &[PathBuf], out_filename: &Path) {
let mut modules = Vec::new();
for obj in objects {
let mut bytes = Vec::new();
File::open(obj).unwrap().read_to_end(&mut bytes).unwrap();
modules.push(load(&bytes));
}
for rlib in rlibs {
for entry in Archive::new(File::open(rlib).unwrap()).entries().unwrap() {
let mut bytes = Vec::new();
entry.unwrap().read_to_end(&mut bytes).unwrap();
modules.push(load(&bytes));
}
}
let mut module_refs = modules.iter_mut().collect::<Vec<_>>();
let result = rspirv_linker::link(
&mut module_refs,
&rspirv_linker::Options {
lib: false,
partial: false,
},
)
.unwrap();
use rspirv::binary::Assemble;
File::create(out_filename)
.unwrap()
.write_all(crate::slice_u32_to_u8(&result.assemble()))
.unwrap();
fn load(bytes: &[u8]) -> rspirv::dr::Module {
let mut loader = rspirv::dr::Loader::new();
rspirv::binary::parse_bytes(&bytes, &mut loader).unwrap();
loader.module()
}
}
pub(crate) fn run_thin(
cgcx: &CodegenContext<SsaBackend>,
cgcx: &CodegenContext<SpirvCodegenBackend>,
modules: Vec<(String, SpirvThinBuffer)>,
cached_modules: Vec<(SerializedModule<SpirvModuleBuffer>, WorkProduct)>,
) -> Result<(Vec<LtoModuleCodegen<SsaBackend>>, Vec<WorkProduct>), FatalError> {
) -> Result<(Vec<LtoModuleCodegen<SpirvCodegenBackend>>, Vec<WorkProduct>), FatalError> {
if cgcx.opts.cg.linker_plugin_lto.enabled() {
unreachable!("We should never reach this case if the LTO step is deferred to the linker");
}