diff --git a/crates/rustc_codegen_spirv/src/builder/intrinsics.rs b/crates/rustc_codegen_spirv/src/builder/intrinsics.rs index deab2b087c..0fcaec8513 100644 --- a/crates/rustc_codegen_spirv/src/builder/intrinsics.rs +++ b/crates/rustc_codegen_spirv/src/builder/intrinsics.rs @@ -9,7 +9,7 @@ use crate::custom_insts::CustomInst; use crate::spirv_type::SpirvType; use rspirv::dr::Operand; use rspirv::spirv::GLOp; -use rustc_codegen_ssa::mir::operand::OperandRef; +use rustc_codegen_ssa::mir::operand::{OperandRef, OperandValue}; use rustc_codegen_ssa::mir::place::PlaceRef; use rustc_codegen_ssa::traits::{BuilderMethods, IntrinsicCallBuilderMethods}; use rustc_middle::ty::layout::LayoutOf; @@ -240,6 +240,26 @@ impl<'a, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'tcx> { sym::ctpop => self.count_ones(args[0].immediate()), sym::bitreverse => self.bit_reverse(args[0].immediate()), + sym::black_box => { + // TODO(LegNeato): do something more sophisticated that prevents DCE + self.tcx + .dcx() + .warn("black_box intrinsic does not prevent optimization in Rust GPU"); + + let layout = self.layout_of(arg_tys[0]); + let llty = layout.spirv_type(self.span(), self); + + match args[0].val { + // Scalars pass through unchanged + OperandValue::Immediate(v) => v, + // Pack scalar pairs to a single SSA aggregate + OperandValue::Pair(..) => args[0].immediate_or_packed_pair(self), + // Lvalues get loaded + OperandValue::Ref(place) => self.load(llty, place.llval, place.align), + // ZSTs become undef of the right type + OperandValue::ZeroSized => self.undef(llty), + } + } sym::bswap => { // https://github.com/KhronosGroup/SPIRV-LLVM/pull/221/files // TODO: Definitely add tests to make sure this impl is right. diff --git a/tests/compiletests/ui/lang/core/intrinsics/black_box.rs b/tests/compiletests/ui/lang/core/intrinsics/black_box.rs new file mode 100644 index 0000000000..4a6c85a76b --- /dev/null +++ b/tests/compiletests/ui/lang/core/intrinsics/black_box.rs @@ -0,0 +1,60 @@ +// build-pass +// compile-flags: -C llvm-args=--disassemble-fn=black_box::disassemble +#![no_std] + +use core::hint::black_box; +use spirv_std::spirv; + +// Minimal kernel that writes the disassembly function result to a buffer +#[spirv(compute(threads(1)))] +pub fn main(#[spirv(descriptor_set = 0, binding = 0, storage_buffer)] out: &mut [u32]) { + let r = disassemble(); + for i in 0..r.len() { + out[i] = r[i]; + } +} + +#[inline(never)] +pub fn disassemble() -> [u32; 12] { + let x = 42i32; + // Immediate: integer scalar passes through unchanged + let y = black_box(x); + + let a = 3.14f32; + // Immediate: float scalar passes through unchanged + let b = black_box(a); + + let v = [1u32, 2, 3, 4]; + // Ref: non-immediate aggregate is loaded from memory + let w = black_box(v); + + // Immediate: constants are immediates + let result = black_box(10) + black_box(20); + + let data = 100u32; + // Immediate (pointer): reference value is an immediate scalar pointer + let ref_data = black_box(&data); + + // Pair: two-element tuple packs into a single SSA aggregate + let pair = (5u32, 6u32); + let pair_bb = black_box(pair); + let pair_sum = pair_bb.0 + pair_bb.1; + + // ZeroSized: unit type becomes `undef` of the right type + let _z = black_box(()); + + [ + y as u32, + f32::to_bits(b), + w[0], + w[1], + w[2], + w[3], + result, + *ref_data, + pair_sum, + 0, + 0, + 0, + ] +} diff --git a/tests/compiletests/ui/lang/core/intrinsics/black_box.stderr b/tests/compiletests/ui/lang/core/intrinsics/black_box.stderr new file mode 100644 index 0000000000..192a37e3a3 --- /dev/null +++ b/tests/compiletests/ui/lang/core/intrinsics/black_box.stderr @@ -0,0 +1,19 @@ +warning: black_box intrinsic does not prevent optimization in Rust GPU + +%1 = OpFunction %2 DontInline %3 +%4 = OpLabel +OpLine %5 32 17 +%6 = OpIAdd %7 %8 %9 +OpLine %5 41 19 +%10 = OpIAdd %7 %11 %12 +OpLine %5 47 8 +%13 = OpBitcast %7 %14 +OpLine %15 1092 17 +%16 = OpBitcast %7 %17 +OpLine %5 46 4 +%18 = OpCompositeConstruct %2 %13 %16 %19 %20 %21 %22 %6 %23 %10 %24 %24 %24 +OpNoLine +OpReturnValue %18 +OpFunctionEnd +warning: 1 warning emitted + diff --git a/tests/difftests/tests/Cargo.lock b/tests/difftests/tests/Cargo.lock index 0b5c36045f..74d87cce66 100644 --- a/tests/difftests/tests/Cargo.lock +++ b/tests/difftests/tests/Cargo.lock @@ -123,6 +123,22 @@ dependencies = [ "difftest", ] +[[package]] +name = "black_box-noop-with" +version = "0.0.0" +dependencies = [ + "difftest", + "spirv-std", +] + +[[package]] +name = "black_box-noop-without" +version = "0.0.0" +dependencies = [ + "difftest", + "spirv-std", +] + [[package]] name = "block" version = "0.1.6" diff --git a/tests/difftests/tests/Cargo.toml b/tests/difftests/tests/Cargo.toml index b2cc9fe3ba..7c74495325 100644 --- a/tests/difftests/tests/Cargo.toml +++ b/tests/difftests/tests/Cargo.toml @@ -38,6 +38,8 @@ members = [ "lang/core/ops/trig_ops/trig_ops-wgsl", "lang/core/ops/vector_swizzle/vector_swizzle-rust", "lang/core/ops/vector_swizzle/vector_swizzle-wgsl", + "lang/core/intrinsics/black_box_noop/with-black-box", + "lang/core/intrinsics/black_box_noop/without-black-box", ] [workspace.package] diff --git a/tests/difftests/tests/lang/core/intrinsics/black_box_noop/with-black-box/Cargo.toml b/tests/difftests/tests/lang/core/intrinsics/black_box_noop/with-black-box/Cargo.toml new file mode 100644 index 0000000000..42c5fe9c79 --- /dev/null +++ b/tests/difftests/tests/lang/core/intrinsics/black_box_noop/with-black-box/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "black_box-noop-with" +edition.workspace = true + +[lints] +workspace = true + +[lib] +crate-type = ["dylib"] + +# GPU deps +[dependencies] +spirv-std.workspace = true + +# CPU deps (for the test harness) +[target.'cfg(not(target_arch = "spirv"))'.dependencies] +difftest.workspace = true diff --git a/tests/difftests/tests/lang/core/intrinsics/black_box_noop/with-black-box/src/lib.rs b/tests/difftests/tests/lang/core/intrinsics/black_box_noop/with-black-box/src/lib.rs new file mode 100644 index 0000000000..5c6732262d --- /dev/null +++ b/tests/difftests/tests/lang/core/intrinsics/black_box_noop/with-black-box/src/lib.rs @@ -0,0 +1,43 @@ +#![no_std] + +use core::hint::black_box; +use spirv_std::spirv; + +#[spirv(compute(threads(1)))] +pub fn main_cs(#[spirv(storage_buffer, descriptor_set = 0, binding = 0)] out: &mut [u32]) { + let x = 42i32; + let y = black_box(x) as u32; + + let a = 3.14f32; + let b = black_box(a).to_bits(); + + let v = [1u32, 2, 3, 4]; + let w = black_box(v); + + let result = black_box(10) + black_box(20); + + let data = 100u32; + let ref_data = black_box(&data); + + let pair = (5u32, 6u32); + let pair_bb = black_box(pair); + let pair_sum = pair_bb.0 + pair_bb.1; + + let _ = black_box(()); + + let values = [ + y, b, w[0], w[1], w[2], w[3], result, *ref_data, pair_sum, 0, 0, 0, + ]; + out[0] = values[0]; + out[1] = values[1]; + out[2] = values[2]; + out[3] = values[3]; + out[4] = values[4]; + out[5] = values[5]; + out[6] = values[6]; + out[7] = values[7]; + out[8] = values[8]; + out[9] = values[9]; + out[10] = values[10]; + out[11] = values[11]; +} diff --git a/tests/difftests/tests/lang/core/intrinsics/black_box_noop/with-black-box/src/main.rs b/tests/difftests/tests/lang/core/intrinsics/black_box_noop/with-black-box/src/main.rs new file mode 100644 index 0000000000..e6ea8d4b46 --- /dev/null +++ b/tests/difftests/tests/lang/core/intrinsics/black_box_noop/with-black-box/src/main.rs @@ -0,0 +1,11 @@ +use difftest::config::{Config, TestMetadata}; +use difftest::scaffold::compute::{RustComputeShader, WgpuComputeTest}; + +fn main() { + let config = Config::from_path(std::env::args().nth(1).unwrap()).unwrap(); + let test = WgpuComputeTest::new(RustComputeShader::default(), [1, 1, 1], 12 * 4); + test.run_test(&config).unwrap(); + config + .write_metadata(&TestMetadata::u32()) + .expect("Failed to write metadata"); +} diff --git a/tests/difftests/tests/lang/core/intrinsics/black_box_noop/without-black-box/Cargo.toml b/tests/difftests/tests/lang/core/intrinsics/black_box_noop/without-black-box/Cargo.toml new file mode 100644 index 0000000000..c1f7123aaf --- /dev/null +++ b/tests/difftests/tests/lang/core/intrinsics/black_box_noop/without-black-box/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "black_box-noop-without" +edition.workspace = true + +[lints] +workspace = true + +[lib] +crate-type = ["dylib"] + +# GPU deps +[dependencies] +spirv-std.workspace = true + +# CPU deps (for the test harness) +[target.'cfg(not(target_arch = "spirv"))'.dependencies] +difftest.workspace = true + diff --git a/tests/difftests/tests/lang/core/intrinsics/black_box_noop/without-black-box/src/lib.rs b/tests/difftests/tests/lang/core/intrinsics/black_box_noop/without-black-box/src/lib.rs new file mode 100644 index 0000000000..238b0bbe63 --- /dev/null +++ b/tests/difftests/tests/lang/core/intrinsics/black_box_noop/without-black-box/src/lib.rs @@ -0,0 +1,39 @@ +#![no_std] + +use spirv_std::spirv; + +#[spirv(compute(threads(1)))] +pub fn main_cs(#[spirv(storage_buffer, descriptor_set = 0, binding = 0)] out: &mut [u32]) { + let x = 42i32; + let y = x as u32; + + let a = 3.14f32; + let b = a.to_bits(); + + let v = [1u32, 2, 3, 4]; + let w = v; + + let result = 10 + 20; + + let data = 100u32; + let ref_data = &data; + + let pair = (5u32, 6u32); + let pair_sum = pair.0 + pair.1; + + let values = [ + y, b, w[0], w[1], w[2], w[3], result, *ref_data, pair_sum, 0, 0, 0, + ]; + out[0] = values[0]; + out[1] = values[1]; + out[2] = values[2]; + out[3] = values[3]; + out[4] = values[4]; + out[5] = values[5]; + out[6] = values[6]; + out[7] = values[7]; + out[8] = values[8]; + out[9] = values[9]; + out[10] = values[10]; + out[11] = values[11]; +} diff --git a/tests/difftests/tests/lang/core/intrinsics/black_box_noop/without-black-box/src/main.rs b/tests/difftests/tests/lang/core/intrinsics/black_box_noop/without-black-box/src/main.rs new file mode 100644 index 0000000000..e6ea8d4b46 --- /dev/null +++ b/tests/difftests/tests/lang/core/intrinsics/black_box_noop/without-black-box/src/main.rs @@ -0,0 +1,11 @@ +use difftest::config::{Config, TestMetadata}; +use difftest::scaffold::compute::{RustComputeShader, WgpuComputeTest}; + +fn main() { + let config = Config::from_path(std::env::args().nth(1).unwrap()).unwrap(); + let test = WgpuComputeTest::new(RustComputeShader::default(), [1, 1, 1], 12 * 4); + test.run_test(&config).unwrap(); + config + .write_metadata(&TestMetadata::u32()) + .expect("Failed to write metadata"); +}