Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 4 additions & 7 deletions crates/rustc_codegen_spirv-types/src/target_spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,10 @@ impl TargetSpecVersion {
/// format the target spec json
pub fn format_spec(&self, target: &SpirvTarget) -> String {
let target_env = target.env();
let extra = match self {
TargetSpecVersion::Rustc_1_76_0 => r#""os": "unknown","#,
_ => r#""crt-static-respected": true,"#,
};
let target_pointer_width = match self {
TargetSpecVersion::Rustc_1_76_0 | TargetSpecVersion::Rustc_1_85_0 => "\"32\"",
TargetSpecVersion::Rustc_1_93_0 => "32",
let (extra, target_pointer_width) = match self {
TargetSpecVersion::Rustc_1_76_0 => (r#""os": "unknown","#, "\"32\""),
TargetSpecVersion::Rustc_1_85_0 => (r#""crt-static-respected": true,"#, "\"32\""),
TargetSpecVersion::Rustc_1_93_0 => (r#""crt-static-respected": true,"#, "32"),
};
format!(
r#"{{
Expand Down
8 changes: 6 additions & 2 deletions crates/rustc_codegen_spirv/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ use std::{env, fs, mem};
/// `cargo publish`. We need to figure out a way to do this properly, but let's hardcode it for now :/
//const REQUIRED_RUST_TOOLCHAIN: &str = include_str!("../../rust-toolchain.toml");
const REQUIRED_RUST_TOOLCHAIN: &str = r#"[toolchain]
channel = "nightly-2025-11-02"
channel = "nightly-2025-11-13"
components = ["rust-src", "rustc-dev", "llvm-tools"]
# commit_hash = bd3ac0330018c23b111bbee176f32c377be7b319"#;
# commit_hash = 01867557cd7dbe256a031a7b8e28d05daecd75ab"#;

fn rustc_output(arg: &str) -> Result<String, Box<dyn Error>> {
let rustc = env::var("RUSTC").unwrap_or_else(|_| "rustc".into());
Expand Down Expand Up @@ -135,6 +135,10 @@ fn generate_pqp_cg_ssa() -> Result<(), Box<dyn Error>> {
}

let in_path = entry.path();

if in_path.ends_with(".DS_Store") {
continue;
}
let out_path = out_dir.join(entry.file_name());

let mut src = fs::read_to_string(in_path)?;
Expand Down
9 changes: 7 additions & 2 deletions crates/rustc_codegen_spirv/src/abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,20 @@ pub(crate) fn provide(providers: &mut Providers) {
// This theoretically then should be fine to leave as C, but, there's no backend hook for
// `FnAbi::adjust_for_cabi`, causing it to panic:
// https://github.com/rust-lang/rust/blob/5fae56971d8487088c0099c82c0a5ce1638b5f62/compiler/rustc_target/src/abi/call/mod.rs#L603
// So, treat any `extern "C"` functions as `extern "unadjusted"`, to be able to compile libcore with arch=spirv.
// So, treat any `extern "C"` functions as `extern "Rust"`, to be able to
// compile libcore with arch=spirv.
//
// NOTE: this used to rewrite to `extern "unadjusted"`, but rustc now
// validates `#[rustc_pass_indirectly_in_non_rustic_abis]` for non-Rust ABIs,
// and `Unadjusted` does not satisfy that requirement.
providers.fn_sig = |tcx, def_id| {
// We can't capture the old fn_sig and just call that, because fn_sig is a `fn`, not a `Fn`, i.e. it can't
// capture variables. Fortunately, the defaults are exposed (thanks rustdoc), so use that instead.
let result = (rustc_interface::DEFAULT_QUERY_PROVIDERS.fn_sig)(tcx, def_id);
result.map_bound(|outer| {
outer.map_bound(|mut inner| {
if let Abi::C { .. } = inner.abi {
inner.abi = Abi::Unadjusted;
inner.abi = Abi::Rust;
Comment on lines +44 to +54
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this the ultimately the right direction. But I'm really curious as to whether this is actually enough of a change for this to be correct. bjorn3's comment on the other PR: #460 (comment) says that assuming that we can't deal with PassMode::Indirect, this could technically still make us use PassMode::Indirect.

Maybe the assertion got passed since it only checks it when the ABI is not rustic, so Abi::Rust passes it (cc https://github.com/rust-lang/rust/blob/0b329f801a09004dacb19aaf09d5cb8b4c51d3f8/compiler/rustc_ty_utils/src/abi.rs#L392-L397), but I do think we might want to look into whether or not we have caused more functions to use PassMode::Indirect and whether we have enough support for that.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So given this, I think this is good enough to go for now, but I'd want to do some testing as to whether something broke. Did the compiletests cover things that should have been covered or are we introducing a regression? I suppose we don't know much but I'm not opposed to just landing this and doing more followup work on it.

}
inner
})
Expand Down
71 changes: 50 additions & 21 deletions crates/rustc_codegen_spirv/src/builder/builder_methods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1917,6 +1917,7 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> {
OperandRef {
val,
layout: place.layout,
move_annotation: None,
}
}

Expand Down Expand Up @@ -3273,29 +3274,57 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> {
// be fixed upstream, so we never see any "function pointer" values being
// created just to perform direct calls.
let (callee_val, result_type, argument_types) = match self.lookup_type(callee.ty) {
SpirvType::Pointer { pointee } => match self.lookup_type(pointee) {
SpirvType::Function {
return_type,
arguments,
} => (
if let SpirvValueKind::FnAddr { function } = callee.kind {
SpirvType::Pointer { pointee } => {
let (pointee_is_function, result_type, argument_types) = match self
.lookup_type(pointee)
{
SpirvType::Function {
return_type,
arguments,
} => (true, return_type, arguments),
// Newer rustc can represent direct call targets as `ptr<void>`,
// with the callee signature carried separately.
_ => {
if let SpirvType::Function {
return_type,
arguments,
} = self.lookup_type(callee_ty)
{
(false, return_type, arguments)
} else {
let Some(fn_abi) = fn_abi else {
bug!(
"call expected `fn` pointer to point to function type, got `{}`",
self.debug_type(pointee)
);
};
let fn_ty = fn_abi.spirv_type(self.span(), self);
match self.lookup_type(fn_ty) {
SpirvType::Function {
return_type,
arguments,
} => (false, return_type, arguments),
_ => bug!("call expected function ABI to lower to function type"),
}
}
}
};

let callee_val = if let SpirvValueKind::FnAddr { function } = callee.kind {
if pointee_is_function {
assert_ty_eq!(self, callee_ty, pointee);
function
}
// Truly indirect call.
else {
let fn_ptr_val = callee.def(self);
self.zombie(fn_ptr_val, "indirect calls are not supported in SPIR-V");
fn_ptr_val
},
return_type,
arguments,
),
_ => bug!(
"call expected `fn` pointer to point to function type, got `{}`",
self.debug_type(pointee)
),
},
function
}
// Truly indirect call.
else {
let fn_ptr_val = callee.def(self);
self.zombie(fn_ptr_val, "indirect calls are not supported in SPIR-V");
fn_ptr_val
};

(callee_val, result_type, argument_types)
}

_ => bug!(
"call expected `fn` pointer type, got `{}`",
Expand Down
2 changes: 1 addition & 1 deletion crates/rustc_codegen_spirv/src/builder/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ impl<'a, 'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'a, 'tcx> {
fn add_coverage(&mut self, _instance: Instance<'tcx>, _kind: &CoverageKind) {}
}

impl<'a, 'tcx> DebugInfoBuilderMethods for Builder<'a, 'tcx> {
impl<'a, 'tcx> DebugInfoBuilderMethods<'_> for Builder<'a, 'tcx> {
fn dbg_var_addr(
&mut self,
_dbg_var: Self::DIVariable,
Expand Down
17 changes: 14 additions & 3 deletions crates/rustc_codegen_spirv/src/codegen_cx/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,15 @@ impl<'tcx> CodegenCx<'tcx> {

// HACK(eddyb) this loads the same `serde_json` used by `rustc_target`.
extern crate serde_json;
fn normalize_target_spec_json(mut json: serde_json::Value) -> serde_json::Value {
// rustc currently rejects this key in external target JSON unless
// the target is `nvptx64` or `amdgcn`, even though the in-memory
// target options for SPIR-V can set it.
if let Some(obj) = json.as_object_mut() {
obj.remove("is-like-gpu");
}
json
}

let expected = &target.rustc_target();
let found = &tcx.sess.target;
Expand All @@ -128,11 +137,13 @@ impl<'tcx> CodegenCx<'tcx> {
TargetTuple::TargetTuple(_) => {
// FIXME(eddyb) this case should be impossible as upstream
// `rustc` doesn't support `spirv-*` targets!
(expected != found).then(|| [expected, found].map(|spec| spec.to_json()))
let expected = normalize_target_spec_json(expected.to_json());
let found = normalize_target_spec_json(found.to_json());
(expected != found).then_some([expected, found])
}
TargetTuple::TargetJson { contents, .. } => {
let expected = expected.to_json();
let found = serde_json::from_str(contents).unwrap();
let expected = normalize_target_spec_json(expected.to_json());
let found = normalize_target_spec_json(serde_json::from_str(contents).unwrap());
(expected != found).then_some([expected, found])
}
}
Expand Down
2 changes: 0 additions & 2 deletions crates/rustc_codegen_spirv/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
// HACK(eddyb) start of `rustc_codegen_ssa` crate-level attributes (see `build.rs`).
#![allow(internal_features)]
#![allow(rustc::diagnostic_outside_of_impl)]
#![allow(rustc::untranslatable_diagnostic)]
#![feature(assert_matches)]
#![feature(box_patterns)]
#![feature(file_buffered)]
#![feature(if_let_guard)]
#![feature(negative_impls)]
#![feature(rustdoc_internals)]
#![feature(string_from_utf8_lossy_owned)]
#![feature(trait_alias)]
#![feature(try_blocks)]
Expand Down
10 changes: 7 additions & 3 deletions crates/rustc_codegen_spirv/src/link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,14 @@ use rustc_session::config::{
};
use rustc_session::output::{check_file_is_writeable, invalid_output_for_target, out_filename};
use rustc_span::Symbol;
use spirv_tools::TargetEnv;
use std::collections::BTreeMap;
use std::ffi::{CString, OsStr};
use std::fs::File;
use std::io::{BufWriter, Read};
use std::iter;
use std::path::{Path, PathBuf};
use std::str::FromStr;
use std::sync::Arc;

pub fn link(
Expand Down Expand Up @@ -337,7 +339,8 @@ fn do_spirv_opt(
opt::{self, Optimizer},
};

let mut optimizer = opt::create(sess.target.options.env.parse().ok());
let target_env = TargetEnv::from_str(sess.target.options.env.desc()).ok();
let mut optimizer = opt::create(target_env);

match sess.opts.optimize {
OptLevel::No => {}
Expand Down Expand Up @@ -399,7 +402,8 @@ fn do_spirv_val(
) {
use spirv_tools::val::{self, Validator};

let validator = val::create(sess.target.options.env.parse().ok());
let target_env = TargetEnv::from_str(sess.target.options.env.desc()).ok();
let validator = val::create(target_env);

if let Err(e) = validator.validate(spv_binary, Some(options)) {
let mut err = sess.dcx().struct_err(e.to_string());
Expand Down Expand Up @@ -498,7 +502,7 @@ fn add_upstream_native_libraries(
fn relevant_lib(sess: &Session, lib: &NativeLib) -> bool {
match lib.cfg {
Some(ref cfg) => {
eval_config_entry(sess, cfg, CRATE_NODE_ID, None, ShouldEmit::ErrorsAndLints).as_bool()
eval_config_entry(sess, cfg, CRATE_NODE_ID, ShouldEmit::ErrorsAndLints).as_bool()
}
None => true,
}
Expand Down
9 changes: 5 additions & 4 deletions crates/rustc_codegen_spirv/src/target.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use rspirv::spirv::MemoryModel;
use rustc_target::spec::{Cc, LinkerFlavor, PanicStrategy, Target, TargetOptions};
use rustc_target::spec::{Arch, Cc, Env, LinkerFlavor, PanicStrategy, Target, TargetOptions};
use spirv_tools::TargetEnv;

const ARCH: &str = "spirv";
Expand Down Expand Up @@ -61,8 +61,9 @@ impl SpirvTarget {
o.emit_debug_gdb_scripts = false;
o.linker_flavor = LinkerFlavor::Unix(Cc::No);
o.panic_strategy = PanicStrategy::Abort;
o.env = self.env.to_string().into();
o.vendor = self.vendor.clone().into();
o.env = Env::Other(self.env.to_string().into());
// Note(@firestar99): not sure if this does anything
o.is_like_gpu = true;
// TODO: Investigate if main_needs_argc_argv is useful (for building exes)
o.main_needs_argc_argv = false;
o
Expand All @@ -74,7 +75,7 @@ impl SpirvTarget {
metadata: Default::default(),
pointer_width: 32,
data_layout: "e-m:e-p:32:32:32-i64:64-n8:16:32:64".into(),
arch: ARCH.into(),
arch: Arch::Other(ARCH.into()),
options: self.init_target_opts(),
}
}
Expand Down
4 changes: 2 additions & 2 deletions rust-toolchain.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[toolchain]
channel = "nightly-2025-11-02"
channel = "nightly-2025-11-13"
components = ["rust-src", "rustc-dev", "llvm-tools"]
# commit_hash = bd3ac0330018c23b111bbee176f32c377be7b319
# commit_hash = 01867557cd7dbe256a031a7b8e28d05daecd75ab

# Whenever changing the nightly channel, update the commit hash above, and
# change `REQUIRED_RUST_TOOLCHAIN` in `crates/rustc_codegen_spirv/build.rs` too.
24 changes: 12 additions & 12 deletions tests/compiletests/ui/arch/debug_printf_type_checking.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -114,21 +114,21 @@ LL - debug_printf!("%u", 11.0_f32);
LL + debug_printf!("%u", 11u32);
|

error[E0277]: the trait bound `{float}: Vector<f32, 2>` is not satisfied
error[E0277]: the trait bound `{float}: spirv_std::Vector<f32, 2>` is not satisfied
--> $DIR/debug_printf_type_checking.rs:23:9
|
LL | debug_printf!("%v2f", 11.0);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Vector<f32, 2>` is not implemented for `{float}`
|
= help: the following other types implement trait `Vector<T, N>`:
`BVec2` implements `Vector<bool, 2>`
`BVec3` implements `Vector<bool, 3>`
`BVec4` implements `Vector<bool, 4>`
`DVec2` implements `Vector<f64, 2>`
`DVec3` implements `Vector<f64, 3>`
`DVec4` implements `Vector<f64, 4>`
`IVec2` implements `Vector<i32, 2>`
`IVec3` implements `Vector<i32, 3>`
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `spirv_std::Vector<f32, 2>` is not implemented for `{float}`
|
= help: the following other types implement trait `spirv_std::Vector<T, N>`:
`BVec2` implements `spirv_std::Vector<bool, 2>`
`BVec3` implements `spirv_std::Vector<bool, 3>`
`BVec4` implements `spirv_std::Vector<bool, 4>`
`DVec2` implements `spirv_std::Vector<f64, 2>`
`DVec3` implements `spirv_std::Vector<f64, 3>`
`DVec4` implements `spirv_std::Vector<f64, 4>`
`IVec2` implements `spirv_std::Vector<i32, 2>`
`IVec3` implements `spirv_std::Vector<i32, 3>`
and 8 others
note: required by a bound in `assert_is_vector`
--> $SPIRV_STD_SRC/debug_printf.rs:11:40
Expand Down
4 changes: 2 additions & 2 deletions tests/compiletests/ui/lang/core/unwrap_or.stderr
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
%1 = OpFunction %2 None %3
%4 = OpLabel
OpLine %5 1041 14
OpLine %5 1042 14
%6 = OpBitcast %7 %8
OpLine %5 1041 8
OpLine %5 1042 8
%9 = OpINotEqual %10 %6 %11
OpNoLine
OpSelectionMerge %12 None
Expand Down
Loading