From 1431c18b9db70feafc64e5096a64e5fefffbed18 Mon Sep 17 00:00:00 2001 From: Ashley Hauck Date: Fri, 30 Apr 2021 09:07:45 +0200 Subject: [PATCH] Move disassemble tests to compiletest (#609) * Move disassemble tests to compiletest * Fix problematic tests * Add newlines --- .github/workflows/test.sh | 1 - Cargo.lock | 2 - .../rustc_codegen_spirv/src/codegen_cx/mod.rs | 114 +++- crates/rustc_codegen_spirv/src/link.rs | 3 + crates/spirv-builder/Cargo.toml | 4 - crates/spirv-builder/src/lib.rs | 3 - crates/spirv-builder/src/test/basic.rs | 496 ------------------ crates/spirv-builder/src/test/mod.rs | 209 -------- tests/ui/dis/add_two_ints.rs | 12 + tests/ui/dis/add_two_ints.stderr | 7 + tests/ui/dis/asm.rs | 19 + tests/ui/dis/asm.stderr | 5 + tests/ui/dis/asm_add_two_ints.rs | 21 + tests/ui/dis/asm_add_two_ints.stderr | 7 + tests/ui/dis/asm_op_decorate.rs | 40 ++ tests/ui/dis/asm_op_decorate.stderr | 22 + tests/ui/dis/complex_image_sample_inst.rs | 62 +++ tests/ui/dis/complex_image_sample_inst.stderr | 12 + tests/ui/dis/custom_entry_point.rs | 10 + tests/ui/dis/custom_entry_point.stderr | 7 + tests/ui/dis/index_user_dst.rs | 10 + tests/ui/dis/index_user_dst.stderr | 30 ++ tests/ui/dis/ptr_copy.rs | 17 + tests/ui/dis/ptr_copy.stderr | 7 + tests/ui/dis/ptr_read.rs | 17 + tests/ui/dis/ptr_read.stderr | 11 + tests/ui/dis/ptr_read_method.rs | 17 + tests/ui/dis/ptr_read_method.stderr | 11 + tests/ui/dis/ptr_write.rs | 17 + tests/ui/dis/ptr_write.stderr | 10 + tests/ui/dis/ptr_write_method.rs | 17 + tests/ui/dis/ptr_write_method.stderr | 10 + tests/ui/dis/unroll_loops.rs | 18 + tests/ui/dis/unroll_loops.stderr | 32 ++ 34 files changed, 564 insertions(+), 716 deletions(-) delete mode 100644 crates/spirv-builder/src/test/basic.rs delete mode 100644 crates/spirv-builder/src/test/mod.rs create mode 100644 tests/ui/dis/add_two_ints.rs create mode 100644 tests/ui/dis/add_two_ints.stderr create mode 100644 tests/ui/dis/asm.rs create mode 100644 tests/ui/dis/asm.stderr create mode 100644 tests/ui/dis/asm_add_two_ints.rs create mode 100644 tests/ui/dis/asm_add_two_ints.stderr create mode 100644 tests/ui/dis/asm_op_decorate.rs create mode 100644 tests/ui/dis/asm_op_decorate.stderr create mode 100644 tests/ui/dis/complex_image_sample_inst.rs create mode 100644 tests/ui/dis/complex_image_sample_inst.stderr create mode 100644 tests/ui/dis/custom_entry_point.rs create mode 100644 tests/ui/dis/custom_entry_point.stderr create mode 100644 tests/ui/dis/index_user_dst.rs create mode 100644 tests/ui/dis/index_user_dst.stderr create mode 100644 tests/ui/dis/ptr_copy.rs create mode 100644 tests/ui/dis/ptr_copy.stderr create mode 100644 tests/ui/dis/ptr_read.rs create mode 100644 tests/ui/dis/ptr_read.stderr create mode 100644 tests/ui/dis/ptr_read_method.rs create mode 100644 tests/ui/dis/ptr_read_method.stderr create mode 100644 tests/ui/dis/ptr_write.rs create mode 100644 tests/ui/dis/ptr_write.stderr create mode 100644 tests/ui/dis/ptr_write_method.rs create mode 100644 tests/ui/dis/ptr_write_method.stderr create mode 100644 tests/ui/dis/unroll_loops.rs create mode 100644 tests/ui/dis/unroll_loops.stderr diff --git a/.github/workflows/test.sh b/.github/workflows/test.sh index ff66ff20b1..c6a318ed10 100755 --- a/.github/workflows/test.sh +++ b/.github/workflows/test.sh @@ -38,7 +38,6 @@ function cargo_test_no_features() { # Core crates cargo_test crates/rustc_codegen_spirv -cargo_test crates/spirv-builder # Examples # See: https://github.com/EmbarkStudios/rust-gpu/issues/84 diff --git a/Cargo.lock b/Cargo.lock index e242e28272..5e9839d932 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2332,9 +2332,7 @@ dependencies = [ name = "spirv-builder" version = "0.4.0-alpha.7" dependencies = [ - "lazy_static", "memchr", - "pretty_assertions", "raw-string", "rustc_codegen_spirv", "serde", diff --git a/crates/rustc_codegen_spirv/src/codegen_cx/mod.rs b/crates/rustc_codegen_spirv/src/codegen_cx/mod.rs index 9955e31c75..dd13c73316 100644 --- a/crates/rustc_codegen_spirv/src/codegen_cx/mod.rs +++ b/crates/rustc_codegen_spirv/src/codegen_cx/mod.rs @@ -214,6 +214,10 @@ impl<'tcx> CodegenCx<'tcx> { pub struct CodegenArgs { pub module_output_type: ModuleOutputType, + pub disassemble: bool, + pub disassemble_fn: Option, + pub disassemble_entry: Option, + pub disassemble_globals: bool, } impl CodegenArgs { @@ -233,10 +237,118 @@ impl CodegenArgs { "single output or multiple output", "[single|multiple]", ); + opts.optflagopt("", "disassemble", "print module to stderr", ""); + opts.optopt("", "disassemble-fn", "print function to stderr", "NAME"); + opts.optopt( + "", + "disassemble-entry", + "print entry point to stderr", + "NAME", + ); + opts.optflagopt("", "disassemble-globals", "print globals to stderr", ""); let matches = opts.parse(args)?; let module_output_type = matches.opt_get_default("module-output", ModuleOutputType::Single)?; - Ok(Self { module_output_type }) + let disassemble = matches.opt_present("disassemble"); + let disassemble_fn = matches.opt_str("disassemble-fn"); + let disassemble_entry = matches.opt_str("disassemble-entry"); + let disassemble_globals = matches.opt_present("disassemble-globals"); + Ok(Self { + module_output_type, + disassemble, + disassemble_fn, + disassemble_entry, + disassemble_globals, + }) + } + + pub fn do_disassemble(&self, module: &Module) { + fn compact_ids(module: &mut rspirv::dr::Function) -> u32 { + let mut remap = std::collections::HashMap::new(); + let mut insert = |current_id: &mut u32| { + let len = remap.len(); + *current_id = *remap.entry(*current_id).or_insert_with(|| len as u32 + 1) + }; + module.all_inst_iter_mut().for_each(|inst| { + if let Some(ref mut result_id) = &mut inst.result_id { + insert(result_id) + } + if let Some(ref mut result_type) = &mut inst.result_type { + insert(result_type) + } + inst.operands.iter_mut().for_each(|op| { + if let Some(w) = op.id_ref_any_mut() { + insert(w) + } + }) + }); + remap.len() as u32 + 1 + } + + use rspirv::binary::Disassemble; + + if self.disassemble { + eprintln!("{}", module.disassemble()); + } + + if let Some(func) = &self.disassemble_fn { + let id = module + .debugs + .iter() + .find(|inst| { + inst.class.opcode == rspirv::spirv::Op::Name + && inst.operands[1].unwrap_literal_string() == func + }) + .unwrap_or_else(|| { + panic!( + "no function with the name `{}` found in:\n{}\n", + func, + module.disassemble() + ) + }) + .operands[0] + .unwrap_id_ref(); + let mut func = module + .functions + .iter() + .find(|f| f.def_id().unwrap() == id) + .unwrap() + .clone(); + // Compact to make IDs more stable + compact_ids(&mut func); + eprintln!("{}", func.disassemble()); + } + + if let Some(entry) = &self.disassemble_entry { + let id = module + .entry_points + .iter() + .find(|inst| inst.operands.last().unwrap().unwrap_literal_string() == entry) + .unwrap_or_else(|| { + panic!( + "no entry point with the name `{}` found in:\n{}\n", + entry, + module.disassemble() + ) + }) + .operands[1] + .unwrap_id_ref(); + let mut func = module + .functions + .iter() + .find(|f| f.def_id().unwrap() == id) + .unwrap() + .clone(); + // Compact to make IDs more stable + compact_ids(&mut func); + eprintln!("{}", func.disassemble()); + } + + if self.disassemble_globals { + for inst in module.global_inst_iter() { + eprintln!("{}", inst.disassemble()); + } + } } } diff --git a/crates/rustc_codegen_spirv/src/link.rs b/crates/rustc_codegen_spirv/src/link.rs index e9ae7613de..f3d76e9e82 100644 --- a/crates/rustc_codegen_spirv/src/link.rs +++ b/crates/rustc_codegen_spirv/src/link.rs @@ -131,10 +131,13 @@ fn link_exe( let spv_binary = do_link(sess, &objects, &rlibs, legalize, emit_multiple_modules); + let cg_args = crate::codegen_cx::CodegenArgs::from_session(sess); + use rspirv::binary::Assemble; match spv_binary { linker::LinkResult::SingleModule(spv_binary) => { post_link_single_module(sess, spv_binary.assemble(), out_filename); + cg_args.do_disassemble(&spv_binary); } linker::LinkResult::MultipleModules(map) => { let mut root_file_name = out_filename.file_name().unwrap().to_owned(); diff --git a/crates/spirv-builder/Cargo.toml b/crates/spirv-builder/Cargo.toml index 9a69d1ca95..459234361f 100644 --- a/crates/spirv-builder/Cargo.toml +++ b/crates/spirv-builder/Cargo.toml @@ -18,7 +18,3 @@ serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" # See comment in lib.rs invoke_rustc for why this is here rustc_codegen_spirv = { path = "../rustc_codegen_spirv", default-features = false } - -[dev-dependencies] -lazy_static = "1.4" -pretty_assertions = "0.7" diff --git a/crates/spirv-builder/src/lib.rs b/crates/spirv-builder/src/lib.rs index 17c2b0aff6..7733f10acf 100644 --- a/crates/spirv-builder/src/lib.rs +++ b/crates/spirv-builder/src/lib.rs @@ -53,9 +53,6 @@ // crate-specific exceptions: #![allow()] -#[cfg(test)] -mod test; - mod depfile; use raw_string::{RawStr, RawString}; diff --git a/crates/spirv-builder/src/test/basic.rs b/crates/spirv-builder/src/test/basic.rs deleted file mode 100644 index d12648d89c..0000000000 --- a/crates/spirv-builder/src/test/basic.rs +++ /dev/null @@ -1,496 +0,0 @@ -use super::{dis_entry_fn, dis_fn, dis_globals, val}; -use std::ffi::OsStr; - -struct SetEnvVar<'a> { - k: &'a OsStr, -} - -impl<'a> SetEnvVar<'a> { - fn new(k: &'a impl AsRef, v: impl AsRef) -> Self { - let k = k.as_ref(); - std::env::set_var(k, v); - Self { k } - } -} - -impl<'a> Drop for SetEnvVar<'a> { - fn drop(&mut self) { - std::env::remove_var(self.k) - } -} - -#[test] -fn custom_entry_point() { - dis_globals( - r#" -#[spirv(fragment(entry_point_name="hello_world"))] -pub fn main() { } -"#, - r#"OpCapability Shader -OpMemoryModel Logical Simple -OpEntryPoint Fragment %1 "hello_world" -OpExecutionMode %1 OriginUpperLeft -OpName %2 "test_project::main" -%3 = OpTypeVoid -%4 = OpTypeFunction %3"#, - ); -} - -#[test] -// blocked on: https://github.com/EmbarkStudios/rust-gpu/issues/69 -#[ignore] -fn no_dce() { - let _var = SetEnvVar::new(&"NO_DCE", "1"); - val(r#" -#[spirv(fragment)] -pub fn no_dce() { -} -"#); -} - -#[test] -fn add_two_ints() { - dis_fn( - r#" -fn add_two_ints(x: u32, y: u32) -> u32 { - x + y -} -#[spirv(fragment)] -pub fn main() { - add_two_ints(2, 3); -} -"#, - "add_two_ints", - r#"%1 = OpFunction %2 None %3 -%4 = OpFunctionParameter %2 -%5 = OpFunctionParameter %2 -%6 = OpLabel -%7 = OpIAdd %2 %4 %5 -OpReturnValue %7 -OpFunctionEnd"#, - ); -} - -#[test] -fn asm() { - dis_fn( - r#" -fn asm() { - unsafe { - asm!( - "%int = OpTypeInt 32 0", - "%scope = OpConstant %int 2", - "%semantics = OpConstant %int 16", - "OpMemoryBarrier %scope %semantics", - ); - } -} -#[spirv(fragment)] -pub fn main() { - asm(); -} -"#, - "asm", - // note: the OpConstants get hoisted out to global in the linker merge pass - r#"%1 = OpFunction %2 None %3 -%4 = OpLabel -OpMemoryBarrier %5 %6 -OpReturn -OpFunctionEnd"#, - ); -} - -#[test] -fn asm_add_two_ints() { - dis_fn( - r#" -fn add_two_ints(x: u32, y: u32) -> u32 { - let result; - unsafe { - asm!( - "{0} = OpIAdd typeof{0} {1} {2}", - out(reg) result, - in(reg) x, - in(reg) y, - ); - } - result -} -#[spirv(fragment)] -pub fn main() { - add_two_ints(2, 3); -} -"#, - "add_two_ints", - r#"%1 = OpFunction %2 None %3 -%4 = OpFunctionParameter %2 -%5 = OpFunctionParameter %2 -%6 = OpLabel -%7 = OpIAdd %2 %4 %5 -OpReturnValue %7 -OpFunctionEnd"#, - ); -} - -#[test] -fn asm_op_decorate() { - // Tests that OpDecorate gets parsed and emitted properly since it's a vararg style instruction - dis_globals( - r#" - fn add_decorate() { - unsafe { - let offset = 1u32; - asm!( - "OpExtension \"SPV_EXT_descriptor_indexing\"", - "OpCapability RuntimeDescriptorArray", - "OpDecorate %image_2d_var DescriptorSet 0", - "OpDecorate %image_2d_var Binding 0", - "%uint = OpTypeInt 32 0", - "%float = OpTypeFloat 32", - "%uint_0 = OpConstant %uint 0", - "%image_2d = OpTypeImage %float Dim2D 0 0 0 1 Unknown", - "%sampled_image_2d = OpTypeSampledImage %image_2d", - "%image_array = OpTypeRuntimeArray %sampled_image_2d", - // NOTE(eddyb) `Generic` is used here because it's the placeholder - // for storage class inference - both of the two `OpTypePointer` - // types below should end up inferring to `UniformConstant`. - "%ptr_image_array = OpTypePointer Generic %image_array", - "%image_2d_var = OpVariable %ptr_image_array UniformConstant", - "%ptr_sampled_image_2d = OpTypePointer Generic %sampled_image_2d", - "", // ^^ type preamble - "%offset = OpLoad _ {0}", - "%24 = OpAccessChain %ptr_sampled_image_2d %image_2d_var %offset", - "%25 = OpLoad %sampled_image_2d %24", - in(reg) &offset, - ); - } - } - #[spirv(fragment)] - pub fn main() { - add_decorate(); - }"#, - r#"OpCapability Shader -OpCapability RuntimeDescriptorArray -OpExtension "SPV_EXT_descriptor_indexing" -OpMemoryModel Logical Simple -OpEntryPoint Fragment %1 "main" -OpExecutionMode %1 OriginUpperLeft -OpName %2 "test_project::add_decorate" -OpName %3 "test_project::main" -OpDecorate %4 ArrayStride 4 -OpDecorate %5 DescriptorSet 0 -OpDecorate %5 Binding 0 -%6 = OpTypeVoid -%7 = OpTypeFunction %6 -%8 = OpTypeInt 32 0 -%9 = OpConstant %8 1 -%10 = OpTypeFloat 32 -%11 = OpTypeImage %10 2D 0 0 0 1 Unknown -%12 = OpTypeSampledImage %11 -%4 = OpTypeRuntimeArray %12 -%13 = OpTypePointer UniformConstant %4 -%5 = OpVariable %13 UniformConstant -%14 = OpTypePointer UniformConstant %12"#, - ); -} - -#[test] -fn unroll_loops() { - dis_fn( - // FIXME(eddyb) use `for _ in 0..10` here when that works. - r#" -#[spirv(unroll_loops)] -fn java_hash_ten_times(mut x: u32, y: u32) -> u32 { - let mut i = 0; - while i < 10 { - x = 31 * x + y; - i += 1; - } - x -} -#[spirv(fragment)] -pub fn main() { - java_hash_ten_times(7, 42); -} -"#, - "java_hash_ten_times", - // NOTE(eddyb) this is very verbose because of the new structurizer - // producing messier control-flow than necessary, but the important part - // being tested is `OpLoopMerge` having `Unroll` as its "Loop Control". - r#"%1 = OpFunction %2 None %3 -%4 = OpFunctionParameter %2 -%5 = OpFunctionParameter %2 -%6 = OpLabel -OpBranch %7 -%7 = OpLabel -OpBranch %8 -%8 = OpLabel -%9 = OpPhi %10 %11 %7 %12 %13 -%14 = OpPhi %2 %4 %7 %15 %13 -%16 = OpPhi %17 %18 %7 %19 %13 -OpLoopMerge %20 %13 Unroll -OpBranchConditional %16 %21 %20 -%21 = OpLabel -%22 = OpSLessThan %17 %9 %23 -OpSelectionMerge %24 None -OpBranchConditional %22 %25 %26 -%25 = OpLabel -%27 = OpIMul %2 %28 %14 -%15 = OpIAdd %2 %27 %5 -%12 = OpIAdd %10 %9 %29 -OpBranch %24 -%26 = OpLabel -OpReturnValue %14 -%24 = OpLabel -%19 = OpPhi %17 %18 %25 -OpBranch %13 -%13 = OpLabel -OpBranch %8 -%20 = OpLabel -OpUnreachable -OpFunctionEnd"#, - ); -} - -#[test] -fn complex_image_sample_inst() { - dis_fn( - r#" - fn sample_proj_lod(coord: glam::Vec4, ddx: glam::Vec2, ddy: glam::Vec2, offset_x: i32, offset_y: i32) -> glam::Vec4 { - unsafe { - let mut result = glam::Vec4::default(); - let index = 0u32; - asm!( - "OpExtension \"SPV_EXT_descriptor_indexing\"", - "OpCapability RuntimeDescriptorArray", - "OpDecorate %image_2d_var DescriptorSet 0", - "OpDecorate %image_2d_var Binding 0", - "%uint = OpTypeInt 32 0", - "%int = OpTypeInt 32 1", - "%float = OpTypeFloat 32", - "%v2int = OpTypeVector %int 2", - "%uint_0 = OpConstant %uint 0", - "%int_0 = OpConstant %int 0", - "%image_2d = OpTypeImage %float Dim2D 0 0 0 1 Unknown", - "%sampled_image_2d = OpTypeSampledImage %image_2d", - "%image_array = OpTypeRuntimeArray %sampled_image_2d", - // NOTE(eddyb) `Generic` is used here because it's the placeholder - // for storage class inference - both of the two `OpTypePointer` - // types below should end up inferring to `UniformConstant`. - "%ptr_image_array = OpTypePointer Generic %image_array", - "%image_2d_var = OpVariable %ptr_image_array UniformConstant", - "%ptr_sampled_image_2d = OpTypePointer Generic %sampled_image_2d", - "", // ^^ type preamble - "%offset = OpLoad _ {1}", - "%24 = OpAccessChain %ptr_sampled_image_2d %image_2d_var %offset", - "%25 = OpLoad %sampled_image_2d %24", - "%coord = OpLoad _ {0}", - "%ddx = OpLoad _ {3}", - "%ddy = OpLoad _ {4}", - "%offset_x = OpLoad _ {5}", - "%offset_y = OpLoad _ {6}", - "%const_offset = OpConstantComposite %v2int %int_0 %int_0", - "%result = OpImageSampleProjExplicitLod _ %25 %coord Grad|ConstOffset %ddx %ddy %const_offset", - "OpStore {2} %result", - in(reg) &coord, - in(reg) &index, - in(reg) &mut result, - in(reg) &ddx, - in(reg) &ddy, - in(reg) &offset_x, - in(reg) &offset_y, - ); - result - } - } - #[spirv(fragment)] - pub fn main() { - sample_proj_lod(glam::Vec4::ZERO, glam::Vec2::ZERO, glam::Vec2::ZERO, 0, 0); - }"#, - "sample_proj_lod", - "%1 = OpFunction %2 None %3 -%4 = OpFunctionParameter %2 -%5 = OpFunctionParameter %6 -%7 = OpFunctionParameter %6 -%8 = OpFunctionParameter %9 -%10 = OpFunctionParameter %9 -%11 = OpLabel -%12 = OpAccessChain %13 %14 %15 -%16 = OpLoad %17 %12 -%18 = OpImageSampleProjExplicitLod %2 %16 %4 Grad|ConstOffset %5 %7 %19 -OpReturnValue %18 -OpFunctionEnd", - ); -} - -/// Helper to generate all of the `ptr_*` tests below, which test that the various -/// ways to use raw pointer `read`/`write`/`copy`, to copy a single value, work, -/// and that the resulting SPIR-V uses either a pair of `OpLoad` and `OpStore`, -/// and/or the `OpCopyMemory` instruction, but *not* `OpCopyMemorySized`. -macro_rules! test_copy_via_raw_ptr { - ($copy_expr:literal => $spirv:literal) => { - dis_fn( - concat!( - r#" - fn copy_via_raw_ptr(src: &f32, dst: &mut f32) { - unsafe { - "#, - $copy_expr, - r#" - } - } - #[spirv(fragment)] - pub fn main(i: f32, o: &mut f32) { - copy_via_raw_ptr(&i, o); - // FIXME(eddyb) above call results in inlining `copy_via_raw_ptr`, - // due to the to `Output` storage classe, so to get the disassembled - // function we also need `Function`-local pointers: - let (src, mut dst) = (0.0, 0.0); - copy_via_raw_ptr(&src, &mut dst); - } -"# - ), - "copy_via_raw_ptr", - concat!( - r#"%1 = OpFunction %2 None %3 - %4 = OpFunctionParameter %5 - %6 = OpFunctionParameter %5 - %7 = OpLabel"#, - $spirv, - r#"OpReturn - OpFunctionEnd"# - ), - ); - }; -} - -#[test] -fn ptr_read() { - test_copy_via_raw_ptr!( - "*dst = core::ptr::read(src)"=> r#" - %8 = OpVariable %5 Function - OpStore %8 %9 - OpCopyMemory %8 %4 - %10 = OpLoad %11 %8 - OpStore %6 %10 - "# - ); -} - -#[test] -fn ptr_read_method() { - test_copy_via_raw_ptr!( - "*dst = (src as *const f32).read()" => r#" - %8 = OpVariable %5 Function - OpStore %8 %9 - OpCopyMemory %8 %4 - %10 = OpLoad %11 %8 - OpStore %6 %10 - "# - ); -} - -#[test] -fn ptr_write() { - test_copy_via_raw_ptr!( - "core::ptr::write(dst, *src)" => r#" - %8 = OpVariable %5 Function - %9 = OpLoad %10 %4 - OpStore %8 %9 - OpCopyMemory %6 %8 - "# - ); -} - -#[test] -fn ptr_write_method() { - test_copy_via_raw_ptr!( - "(dst as *mut f32).write(*src)" => r#" - %8 = OpVariable %5 Function - %9 = OpLoad %10 %4 - OpStore %8 %9 - OpCopyMemory %6 %8 - "# - ); -} - -#[test] -fn ptr_copy() { - test_copy_via_raw_ptr!( - "core::ptr::copy(src, dst, 1)" => r#" - OpCopyMemory %6 %4 - "# - ); -} - -#[test] -// FIXME(eddyb) doesn't work because `<*const T>::copy_to` is a method that wraps -// the actual `core::ptr::copy` intrinsic - this requires either MIR inlining, or -// making the methods themselves intrinsic (via attributes instead of pseudo-ABI). -#[ignore] -fn ptr_copy_to_method() { - test_copy_via_raw_ptr!( - "(src as *const f32).copy_to(dst, 1)" => r#" - OpCopyMemory %6 %4 - "# - ); -} - -#[test] -// FIXME(eddyb) doesn't work because `<*mut T>::copy_from` is a method that wraps -// the actual `core::ptr::copy` intrinsic - this requires either MIR inlining, or -// making the methods themselves intrinsic (via attributes instead of pseudo-ABI). -#[ignore] -fn ptr_copy_from_method() { - test_copy_via_raw_ptr!( - "(dst as *mut f32).copy_from(src, 1)" => r#" - OpCopyMemory %6 %4 - "# - ); -} - -#[test] -fn index_user_dst() { - dis_entry_fn( - r#" -#[spirv(fragment)] -pub fn main( - #[spirv(storage_buffer, descriptor_set = 0, binding = 0)] slice: &mut [f32], -) { - let float: f32 = slice[0]; - let _ = float; -} - "#, - "main", - r#"%1 = OpFunction %2 None %3 -%4 = OpLabel -%5 = OpAccessChain %6 %7 %8 -%9 = OpArrayLength %10 %7 0 -%11 = OpCompositeInsert %12 %5 %13 0 -%14 = OpCompositeInsert %12 %9 %11 1 -%15 = OpULessThan %16 %8 %9 -OpSelectionMerge %17 None -OpBranchConditional %15 %18 %19 -%18 = OpLabel -%20 = OpInBoundsAccessChain %21 %5 %8 -%22 = OpLoad %23 %20 -OpReturn -%19 = OpLabel -OpBranch %24 -%24 = OpLabel -OpBranch %25 -%25 = OpLabel -%26 = OpPhi %16 %27 %24 %27 %28 -OpLoopMerge %29 %28 None -OpBranchConditional %26 %30 %29 -%30 = OpLabel -OpBranch %28 -%28 = OpLabel -OpBranch %25 -%29 = OpLabel -OpUnreachable -%17 = OpLabel -OpUnreachable -OpFunctionEnd"#, - ) -} diff --git a/crates/spirv-builder/src/test/mod.rs b/crates/spirv-builder/src/test/mod.rs deleted file mode 100644 index f4a204d605..0000000000 --- a/crates/spirv-builder/src/test/mod.rs +++ /dev/null @@ -1,209 +0,0 @@ -mod basic; - -use lazy_static::lazy_static; -use rustc_codegen_spirv::rspirv; -use std::error::Error; -use std::path::{Path, PathBuf}; -use std::sync::{Mutex, MutexGuard}; - -// https://github.com/colin-kiegel/rust-pretty-assertions/issues/24 -#[derive(PartialEq, Eq)] -pub struct PrettyString<'a>(pub &'a str); -/// Make diff to display string as multi-line string -impl<'a> std::fmt::Debug for PrettyString<'a> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_str(self.0) - } -} - -// Tests need to run serially, since they write project files to disk and whatnot. We don't want to -// create a new temp dir for every test, though, since then every test would need to build libcore. -// We could require the user to pass --thread-count 1 to cargo test, but that affects other tests. -// So make a global mutex wrapping every test. -lazy_static! { - static ref GLOBAL_MUTEX: Mutex<()> = Mutex::new(()); -} - -fn global_lock() -> MutexGuard<'static, ()> { - match GLOBAL_MUTEX.lock() { - Ok(guard) => guard, - // we don't care about poison - a previous test may have paniced, but that's okay - Err(poisoned) => poisoned.into_inner(), - } -} - -static CARGO_TOML: &str = r#"[package] -name = "test-project" -version = "0.1.0" -authors = ["Embark "] -edition = "2018" - -[lib] -crate-type = ["dylib"] - -[profile.dev] -overflow-checks = false -debug-assertions = false - -[dependencies] -spirv-std = { path = "../../crates/spirv-std", features=["const-generics"] } -glam = { git = "https://github.com/EmbarkStudios/glam-rs.git", rev="7476a96", default-features=false, features = ["libm", "scalar-math"] } - -[patch.crates-io.spirv-std] -path="../../crates/spirv-std" - -[workspace] -"#; - -static SRC_PREFIX: &str = r#"#![no_std] -#![feature(register_attr, asm, ptr_internals)] -#![register_attr(spirv)] -#![deny(warnings)] - -#[allow(unused_imports)] -use spirv_std::{*, num_traits::Float as _ }; -"#; - -fn setup(src: &str) -> Result> { - let project = Path::new("../../target/test-spirv").to_owned(); - let cargo_toml = project.join("Cargo.toml"); - let lib_rs = project.join("src/lib.rs"); - std::fs::create_dir_all(lib_rs.parent().unwrap())?; - // don't write cargo.toml if unchanged, so it doesn't get deps refreshed - if std::fs::read(&cargo_toml).map_or(true, |b| b != CARGO_TOML.as_bytes()) { - std::fs::write(cargo_toml, CARGO_TOML)?; - } - std::fs::write(lib_rs, format!("{}{}", SRC_PREFIX, src))?; - Ok(project) -} - -fn build(src: &str) -> PathBuf { - let project = setup(src).expect("Failed to set up project"); - crate::SpirvBuilder::new(&project, "spirv-unknown-spv1.3") - .print_metadata(false) - .release(false) - .build() - .expect("Failed to build test") -} - -fn read_module(path: &Path) -> Result> { - let bytes = std::fs::read(path)?; - let mut loader = rspirv::dr::Loader::new(); - rspirv::binary::parse_bytes(&bytes, &mut loader)?; - Ok(loader.module()) -} - -fn val(src: &str) { - let _lock = global_lock(); - // spirv-val is included in building - build(src); -} - -fn assert_str_eq(expected: &str, result: &str) { - let expected = expected - .split('\n') - .map(|l| l.trim()) - .collect::>() - .join("\n"); - - let result = result - .split('\n') - .map(|l| l.trim().replace(" ", " ")) // rspirv outputs multiple spaces between operands - .collect::>() - .join("\n"); - - pretty_assertions::assert_eq!(PrettyString(&expected), PrettyString(&result)) -} - -fn dis_fn(src: &str, func: &str, expect: &str) { - let _lock = global_lock(); - let module = read_module(&build(src)).unwrap(); - let abs_func_path = format!("test_project::{}", func); - let id = module - .debugs - .iter() - .find(|inst| { - inst.class.opcode == rspirv::spirv::Op::Name - && inst.operands[1].unwrap_literal_string() == abs_func_path - }) - .unwrap_or_else(|| { - panic!( - "no function with the name `{}` found in:\n{}\n", - abs_func_path, - module.disassemble() - ) - }) - .operands[0] - .unwrap_id_ref(); - let mut func = module - .functions - .into_iter() - .find(|f| f.def_id().unwrap() == id) - .unwrap(); - // Compact to make IDs more stable - compact_ids(&mut func); - use rspirv::binary::Disassemble; - assert_str_eq(expect, &func.disassemble()) -} - -fn dis_entry_fn(src: &str, func: &str, expect: &str) { - let _lock = global_lock(); - let module = read_module(&build(src)).unwrap(); - let id = module - .entry_points - .iter() - .find(|inst| inst.operands.last().unwrap().unwrap_literal_string() == func) - .unwrap_or_else(|| { - panic!( - "no entry point with the name `{}` found in:\n{}\n", - func, - module.disassemble() - ) - }) - .operands[1] - .unwrap_id_ref(); - let mut func = module - .functions - .into_iter() - .find(|f| f.def_id().unwrap() == id) - .unwrap(); - // Compact to make IDs more stable - compact_ids(&mut func); - use rspirv::binary::Disassemble; - assert_str_eq(expect, &func.disassemble()) -} - -fn dis_globals(src: &str, expect: &str) { - let _lock = global_lock(); - let module = read_module(&build(src)).unwrap(); - - use rspirv::binary::Disassemble; - let dis = module - .global_inst_iter() - .map(|inst| inst.disassemble()) - .collect::>() - .join("\n"); - assert_str_eq(expect, &dis); -} - -fn compact_ids(module: &mut rspirv::dr::Function) -> u32 { - let mut remap = std::collections::HashMap::new(); - let mut insert = |current_id: &mut u32| { - let len = remap.len(); - *current_id = *remap.entry(*current_id).or_insert_with(|| len as u32 + 1) - }; - module.all_inst_iter_mut().for_each(|inst| { - if let Some(ref mut result_id) = &mut inst.result_id { - insert(result_id) - } - if let Some(ref mut result_type) = &mut inst.result_type { - insert(result_type) - } - inst.operands.iter_mut().for_each(|op| { - if let Some(w) = op.id_ref_any_mut() { - insert(w) - } - }) - }); - remap.len() as u32 + 1 -} diff --git a/tests/ui/dis/add_two_ints.rs b/tests/ui/dis/add_two_ints.rs new file mode 100644 index 0000000000..9f441a3ee3 --- /dev/null +++ b/tests/ui/dis/add_two_ints.rs @@ -0,0 +1,12 @@ +// build-pass +// compile-flags: -C llvm-args=--disassemble-fn=add_two_ints::add_two_ints + +use spirv_std as _; + +fn add_two_ints(x: u32, y: u32) -> u32 { + x + y +} +#[spirv(fragment)] +pub fn main() { + add_two_ints(2, 3); +} diff --git a/tests/ui/dis/add_two_ints.stderr b/tests/ui/dis/add_two_ints.stderr new file mode 100644 index 0000000000..2873f8a647 --- /dev/null +++ b/tests/ui/dis/add_two_ints.stderr @@ -0,0 +1,7 @@ +%1 = OpFunction %2 None %3 +%4 = OpFunctionParameter %2 +%5 = OpFunctionParameter %2 +%6 = OpLabel +%7 = OpIAdd %2 %4 %5 +OpReturnValue %7 +OpFunctionEnd diff --git a/tests/ui/dis/asm.rs b/tests/ui/dis/asm.rs new file mode 100644 index 0000000000..063c4d4fbc --- /dev/null +++ b/tests/ui/dis/asm.rs @@ -0,0 +1,19 @@ +// build-pass +// compile-flags: -C llvm-args=--disassemble-fn=asm::asm + +use spirv_std as _; + +fn asm() { + unsafe { + asm!( + "%int = OpTypeInt 32 0", + "%scope = OpConstant %int 3", + "%semantics = OpConstant %int 72", + "OpMemoryBarrier %scope %semantics", + ); + } +} +#[spirv(fragment)] +pub fn main() { + asm(); +} diff --git a/tests/ui/dis/asm.stderr b/tests/ui/dis/asm.stderr new file mode 100644 index 0000000000..7fa7f66ab6 --- /dev/null +++ b/tests/ui/dis/asm.stderr @@ -0,0 +1,5 @@ +%1 = OpFunction %2 None %3 +%4 = OpLabel +OpMemoryBarrier %5 %6 +OpReturn +OpFunctionEnd diff --git a/tests/ui/dis/asm_add_two_ints.rs b/tests/ui/dis/asm_add_two_ints.rs new file mode 100644 index 0000000000..5a535a23d7 --- /dev/null +++ b/tests/ui/dis/asm_add_two_ints.rs @@ -0,0 +1,21 @@ +// build-pass +// compile-flags: -C llvm-args=--disassemble-fn=asm_add_two_ints::add_two_ints + +use spirv_std as _; + +fn add_two_ints(x: u32, y: u32) -> u32 { + let result; + unsafe { + asm!( + "{0} = OpIAdd typeof{0} {1} {2}", + out(reg) result, + in(reg) x, + in(reg) y, + ); + } + result +} +#[spirv(fragment)] +pub fn main() { + add_two_ints(2, 3); +} diff --git a/tests/ui/dis/asm_add_two_ints.stderr b/tests/ui/dis/asm_add_two_ints.stderr new file mode 100644 index 0000000000..2873f8a647 --- /dev/null +++ b/tests/ui/dis/asm_add_two_ints.stderr @@ -0,0 +1,7 @@ +%1 = OpFunction %2 None %3 +%4 = OpFunctionParameter %2 +%5 = OpFunctionParameter %2 +%6 = OpLabel +%7 = OpIAdd %2 %4 %5 +OpReturnValue %7 +OpFunctionEnd diff --git a/tests/ui/dis/asm_op_decorate.rs b/tests/ui/dis/asm_op_decorate.rs new file mode 100644 index 0000000000..322b088dfc --- /dev/null +++ b/tests/ui/dis/asm_op_decorate.rs @@ -0,0 +1,40 @@ +// build-pass +// compile-flags: -C llvm-args=--disassemble-globals +// normalize-stderr-test "OpCapability VulkanMemoryModel\n" -> "" +// normalize-stderr-test "OpExtension .SPV_KHR_vulkan_memory_model.\n" -> "" +// normalize-stderr-test "OpMemoryModel Logical Vulkan" -> "OpMemoryModel Logical Simple" + +use spirv_std as _; + +fn add_decorate() { + unsafe { + let offset = 1u32; + asm!( + "OpExtension \"SPV_EXT_descriptor_indexing\"", + "OpCapability RuntimeDescriptorArray", + "OpDecorate %image_2d_var DescriptorSet 0", + "OpDecorate %image_2d_var Binding 0", + "%uint = OpTypeInt 32 0", + "%float = OpTypeFloat 32", + "%uint_0 = OpConstant %uint 0", + "%image_2d = OpTypeImage %float Dim2D 0 0 0 1 Unknown", + "%sampled_image_2d = OpTypeSampledImage %image_2d", + "%image_array = OpTypeRuntimeArray %sampled_image_2d", + // NOTE(eddyb) `Generic` is used here because it's the placeholder + // for storage class inference - both of the two `OpTypePointer` + // types below should end up inferring to `UniformConstant`. + "%ptr_image_array = OpTypePointer Generic %image_array", + "%image_2d_var = OpVariable %ptr_image_array UniformConstant", + "%ptr_sampled_image_2d = OpTypePointer Generic %sampled_image_2d", + "", // ^^ type preamble + "%offset = OpLoad _ {0}", + "%24 = OpAccessChain %ptr_sampled_image_2d %image_2d_var %offset", + "%25 = OpLoad %sampled_image_2d %24", + in(reg) &offset, + ); + } +} +#[spirv(fragment)] +pub fn main() { + add_decorate(); +} diff --git a/tests/ui/dis/asm_op_decorate.stderr b/tests/ui/dis/asm_op_decorate.stderr new file mode 100644 index 0000000000..c16bf3264e --- /dev/null +++ b/tests/ui/dis/asm_op_decorate.stderr @@ -0,0 +1,22 @@ +OpCapability Shader +OpCapability RuntimeDescriptorArray +OpExtension "SPV_EXT_descriptor_indexing" +OpMemoryModel Logical Simple +OpEntryPoint Fragment %1 "main" +OpExecutionMode %1 OriginUpperLeft +OpName %2 "asm_op_decorate::add_decorate" +OpName %3 "asm_op_decorate::main" +OpDecorate %4 ArrayStride 4 +OpDecorate %5 DescriptorSet 0 +OpDecorate %5 Binding 0 +%6 = OpTypeVoid +%7 = OpTypeFunction %6 +%8 = OpTypeInt 32 0 +%9 = OpConstant %8 1 +%10 = OpTypeFloat 32 +%11 = OpTypeImage %10 2D 0 0 0 1 Unknown +%12 = OpTypeSampledImage %11 +%4 = OpTypeRuntimeArray %12 +%13 = OpTypePointer UniformConstant %4 +%5 = OpVariable %13 UniformConstant +%14 = OpTypePointer UniformConstant %12 diff --git a/tests/ui/dis/complex_image_sample_inst.rs b/tests/ui/dis/complex_image_sample_inst.rs new file mode 100644 index 0000000000..197c0c52a4 --- /dev/null +++ b/tests/ui/dis/complex_image_sample_inst.rs @@ -0,0 +1,62 @@ +// build-pass +// compile-flags: -C llvm-args=--disassemble-fn=complex_image_sample_inst::sample_proj_lod + +use spirv_std as _; + +fn sample_proj_lod( + coord: glam::Vec4, + ddx: glam::Vec2, + ddy: glam::Vec2, + offset_x: i32, + offset_y: i32, +) -> glam::Vec4 { + unsafe { + let mut result = glam::Vec4::default(); + let index = 0u32; + asm!( + "OpExtension \"SPV_EXT_descriptor_indexing\"", + "OpCapability RuntimeDescriptorArray", + "OpDecorate %image_2d_var DescriptorSet 0", + "OpDecorate %image_2d_var Binding 0", + "%uint = OpTypeInt 32 0", + "%int = OpTypeInt 32 1", + "%float = OpTypeFloat 32", + "%v2int = OpTypeVector %int 2", + "%uint_0 = OpConstant %uint 0", + "%int_0 = OpConstant %int 0", + "%image_2d = OpTypeImage %float Dim2D 0 0 0 1 Unknown", + "%sampled_image_2d = OpTypeSampledImage %image_2d", + "%image_array = OpTypeRuntimeArray %sampled_image_2d", + // NOTE(eddyb) `Generic` is used here because it's the placeholder + // for storage class inference - both of the two `OpTypePointer` + // types below should end up inferring to `UniformConstant`. + "%ptr_image_array = OpTypePointer Generic %image_array", + "%image_2d_var = OpVariable %ptr_image_array UniformConstant", + "%ptr_sampled_image_2d = OpTypePointer Generic %sampled_image_2d", + "", // ^^ type preamble + "%offset = OpLoad _ {1}", + "%24 = OpAccessChain %ptr_sampled_image_2d %image_2d_var %offset", + "%25 = OpLoad %sampled_image_2d %24", + "%coord = OpLoad _ {0}", + "%ddx = OpLoad _ {3}", + "%ddy = OpLoad _ {4}", + "%offset_x = OpLoad _ {5}", + "%offset_y = OpLoad _ {6}", + "%const_offset = OpConstantComposite %v2int %int_0 %int_0", + "%result = OpImageSampleProjExplicitLod _ %25 %coord Grad|ConstOffset %ddx %ddy %const_offset", + "OpStore {2} %result", + in(reg) &coord, + in(reg) &index, + in(reg) &mut result, + in(reg) &ddx, + in(reg) &ddy, + in(reg) &offset_x, + in(reg) &offset_y, + ); + result + } +} +#[spirv(fragment)] +pub fn main() { + sample_proj_lod(glam::Vec4::ZERO, glam::Vec2::ZERO, glam::Vec2::ZERO, 0, 0); +} diff --git a/tests/ui/dis/complex_image_sample_inst.stderr b/tests/ui/dis/complex_image_sample_inst.stderr new file mode 100644 index 0000000000..e1b712548d --- /dev/null +++ b/tests/ui/dis/complex_image_sample_inst.stderr @@ -0,0 +1,12 @@ +%1 = OpFunction %2 None %3 +%4 = OpFunctionParameter %2 +%5 = OpFunctionParameter %6 +%7 = OpFunctionParameter %6 +%8 = OpFunctionParameter %9 +%10 = OpFunctionParameter %9 +%11 = OpLabel +%12 = OpAccessChain %13 %14 %15 +%16 = OpLoad %17 %12 +%18 = OpImageSampleProjExplicitLod %2 %16 %4 Grad|ConstOffset %5 %7 %19 +OpReturnValue %18 +OpFunctionEnd diff --git a/tests/ui/dis/custom_entry_point.rs b/tests/ui/dis/custom_entry_point.rs new file mode 100644 index 0000000000..1ddcc5c460 --- /dev/null +++ b/tests/ui/dis/custom_entry_point.rs @@ -0,0 +1,10 @@ +// build-pass +// compile-flags: -C llvm-args=--disassemble-globals +// normalize-stderr-test "OpCapability VulkanMemoryModel\n" -> "" +// normalize-stderr-test "OpExtension .SPV_KHR_vulkan_memory_model.\n" -> "" +// normalize-stderr-test "OpMemoryModel Logical Vulkan" -> "OpMemoryModel Logical Simple" + +use spirv_std as _; + +#[spirv(fragment(entry_point_name = "hello_world"))] +pub fn main() {} diff --git a/tests/ui/dis/custom_entry_point.stderr b/tests/ui/dis/custom_entry_point.stderr new file mode 100644 index 0000000000..93e7e8ec3c --- /dev/null +++ b/tests/ui/dis/custom_entry_point.stderr @@ -0,0 +1,7 @@ +OpCapability Shader +OpMemoryModel Logical Simple +OpEntryPoint Fragment %1 "hello_world" +OpExecutionMode %1 OriginUpperLeft +OpName %2 "custom_entry_point::main" +%3 = OpTypeVoid +%4 = OpTypeFunction %3 diff --git a/tests/ui/dis/index_user_dst.rs b/tests/ui/dis/index_user_dst.rs new file mode 100644 index 0000000000..e680eab04f --- /dev/null +++ b/tests/ui/dis/index_user_dst.rs @@ -0,0 +1,10 @@ +// build-pass +// compile-flags: -C llvm-args=--disassemble-entry=main + +use spirv_std as _; + +#[spirv(fragment)] +pub fn main(#[spirv(storage_buffer, descriptor_set = 0, binding = 0)] slice: &mut [f32]) { + let float: f32 = slice[0]; + let _ = float; +} diff --git a/tests/ui/dis/index_user_dst.stderr b/tests/ui/dis/index_user_dst.stderr new file mode 100644 index 0000000000..56fd595f39 --- /dev/null +++ b/tests/ui/dis/index_user_dst.stderr @@ -0,0 +1,30 @@ +%1 = OpFunction %2 None %3 +%4 = OpLabel +%5 = OpAccessChain %6 %7 %8 +%9 = OpArrayLength %10 %7 0 +%11 = OpCompositeInsert %12 %5 %13 0 +%14 = OpCompositeInsert %12 %9 %11 1 +%15 = OpULessThan %16 %8 %9 +OpSelectionMerge %17 None +OpBranchConditional %15 %18 %19 +%18 = OpLabel +%20 = OpInBoundsAccessChain %21 %5 %8 +%22 = OpLoad %23 %20 +OpReturn +%19 = OpLabel +OpBranch %24 +%24 = OpLabel +OpBranch %25 +%25 = OpLabel +%26 = OpPhi %16 %27 %24 %27 %28 +OpLoopMerge %29 %28 None +OpBranchConditional %26 %30 %29 +%30 = OpLabel +OpBranch %28 +%28 = OpLabel +OpBranch %25 +%29 = OpLabel +OpUnreachable +%17 = OpLabel +OpUnreachable +OpFunctionEnd diff --git a/tests/ui/dis/ptr_copy.rs b/tests/ui/dis/ptr_copy.rs new file mode 100644 index 0000000000..58ef5455ea --- /dev/null +++ b/tests/ui/dis/ptr_copy.rs @@ -0,0 +1,17 @@ +// build-pass +// compile-flags: -C llvm-args=--disassemble-fn=ptr_copy::copy_via_raw_ptr + +use spirv_std as _; + +fn copy_via_raw_ptr(src: &f32, dst: &mut f32) { + unsafe { core::ptr::copy(src, dst, 1) } +} +#[spirv(fragment)] +pub fn main(i: f32, o: &mut f32) { + copy_via_raw_ptr(&i, o); + // FIXME(eddyb) above call results in inlining `copy_via_raw_ptr`, + // due to the to `Output` storage classe, so to get the disassembled + // function we also need `Function`-local pointers: + let (src, mut dst) = (0.0, 0.0); + copy_via_raw_ptr(&src, &mut dst); +} diff --git a/tests/ui/dis/ptr_copy.stderr b/tests/ui/dis/ptr_copy.stderr new file mode 100644 index 0000000000..aa92183bef --- /dev/null +++ b/tests/ui/dis/ptr_copy.stderr @@ -0,0 +1,7 @@ +%1 = OpFunction %2 None %3 +%4 = OpFunctionParameter %5 +%6 = OpFunctionParameter %5 +%7 = OpLabel +OpCopyMemory %6 %4 +OpReturn +OpFunctionEnd diff --git a/tests/ui/dis/ptr_read.rs b/tests/ui/dis/ptr_read.rs new file mode 100644 index 0000000000..4229ab0f86 --- /dev/null +++ b/tests/ui/dis/ptr_read.rs @@ -0,0 +1,17 @@ +// build-pass +// compile-flags: -C llvm-args=--disassemble-fn=ptr_read::copy_via_raw_ptr + +use spirv_std as _; + +fn copy_via_raw_ptr(src: &f32, dst: &mut f32) { + unsafe { *dst = core::ptr::read(src) } +} +#[spirv(fragment)] +pub fn main(i: f32, o: &mut f32) { + copy_via_raw_ptr(&i, o); + // FIXME(eddyb) above call results in inlining `copy_via_raw_ptr`, + // due to the to `Output` storage classe, so to get the disassembled + // function we also need `Function`-local pointers: + let (src, mut dst) = (0.0, 0.0); + copy_via_raw_ptr(&src, &mut dst); +} diff --git a/tests/ui/dis/ptr_read.stderr b/tests/ui/dis/ptr_read.stderr new file mode 100644 index 0000000000..3841406b1e --- /dev/null +++ b/tests/ui/dis/ptr_read.stderr @@ -0,0 +1,11 @@ +%1 = OpFunction %2 None %3 +%4 = OpFunctionParameter %5 +%6 = OpFunctionParameter %5 +%7 = OpLabel +%8 = OpVariable %5 Function +OpStore %8 %9 +OpCopyMemory %8 %4 +%10 = OpLoad %11 %8 +OpStore %6 %10 +OpReturn +OpFunctionEnd diff --git a/tests/ui/dis/ptr_read_method.rs b/tests/ui/dis/ptr_read_method.rs new file mode 100644 index 0000000000..1dd6174f28 --- /dev/null +++ b/tests/ui/dis/ptr_read_method.rs @@ -0,0 +1,17 @@ +// build-pass +// compile-flags: -C llvm-args=--disassemble-fn=ptr_read_method::copy_via_raw_ptr + +use spirv_std as _; + +fn copy_via_raw_ptr(src: &f32, dst: &mut f32) { + unsafe { *dst = (src as *const f32).read() } +} +#[spirv(fragment)] +pub fn main(i: f32, o: &mut f32) { + copy_via_raw_ptr(&i, o); + // FIXME(eddyb) above call results in inlining `copy_via_raw_ptr`, + // due to the to `Output` storage classe, so to get the disassembled + // function we also need `Function`-local pointers: + let (src, mut dst) = (0.0, 0.0); + copy_via_raw_ptr(&src, &mut dst); +} diff --git a/tests/ui/dis/ptr_read_method.stderr b/tests/ui/dis/ptr_read_method.stderr new file mode 100644 index 0000000000..3841406b1e --- /dev/null +++ b/tests/ui/dis/ptr_read_method.stderr @@ -0,0 +1,11 @@ +%1 = OpFunction %2 None %3 +%4 = OpFunctionParameter %5 +%6 = OpFunctionParameter %5 +%7 = OpLabel +%8 = OpVariable %5 Function +OpStore %8 %9 +OpCopyMemory %8 %4 +%10 = OpLoad %11 %8 +OpStore %6 %10 +OpReturn +OpFunctionEnd diff --git a/tests/ui/dis/ptr_write.rs b/tests/ui/dis/ptr_write.rs new file mode 100644 index 0000000000..e05b1d12ac --- /dev/null +++ b/tests/ui/dis/ptr_write.rs @@ -0,0 +1,17 @@ +// build-pass +// compile-flags: -C llvm-args=--disassemble-fn=ptr_write::copy_via_raw_ptr + +use spirv_std as _; + +fn copy_via_raw_ptr(src: &f32, dst: &mut f32) { + unsafe { core::ptr::write(dst, *src) } +} +#[spirv(fragment)] +pub fn main(i: f32, o: &mut f32) { + copy_via_raw_ptr(&i, o); + // FIXME(eddyb) above call results in inlining `copy_via_raw_ptr`, + // due to the to `Output` storage classe, so to get the disassembled + // function we also need `Function`-local pointers: + let (src, mut dst) = (0.0, 0.0); + copy_via_raw_ptr(&src, &mut dst); +} diff --git a/tests/ui/dis/ptr_write.stderr b/tests/ui/dis/ptr_write.stderr new file mode 100644 index 0000000000..c7e99d9949 --- /dev/null +++ b/tests/ui/dis/ptr_write.stderr @@ -0,0 +1,10 @@ +%1 = OpFunction %2 None %3 +%4 = OpFunctionParameter %5 +%6 = OpFunctionParameter %5 +%7 = OpLabel +%8 = OpVariable %5 Function +%9 = OpLoad %10 %4 +OpStore %8 %9 +OpCopyMemory %6 %8 +OpReturn +OpFunctionEnd diff --git a/tests/ui/dis/ptr_write_method.rs b/tests/ui/dis/ptr_write_method.rs new file mode 100644 index 0000000000..7107e1a39f --- /dev/null +++ b/tests/ui/dis/ptr_write_method.rs @@ -0,0 +1,17 @@ +// build-pass +// compile-flags: -C llvm-args=--disassemble-fn=ptr_write_method::copy_via_raw_ptr + +use spirv_std as _; + +fn copy_via_raw_ptr(src: &f32, dst: &mut f32) { + unsafe { (dst as *mut f32).write(*src) } +} +#[spirv(fragment)] +pub fn main(i: f32, o: &mut f32) { + copy_via_raw_ptr(&i, o); + // FIXME(eddyb) above call results in inlining `copy_via_raw_ptr`, + // due to the to `Output` storage classe, so to get the disassembled + // function we also need `Function`-local pointers: + let (src, mut dst) = (0.0, 0.0); + copy_via_raw_ptr(&src, &mut dst); +} diff --git a/tests/ui/dis/ptr_write_method.stderr b/tests/ui/dis/ptr_write_method.stderr new file mode 100644 index 0000000000..c7e99d9949 --- /dev/null +++ b/tests/ui/dis/ptr_write_method.stderr @@ -0,0 +1,10 @@ +%1 = OpFunction %2 None %3 +%4 = OpFunctionParameter %5 +%6 = OpFunctionParameter %5 +%7 = OpLabel +%8 = OpVariable %5 Function +%9 = OpLoad %10 %4 +OpStore %8 %9 +OpCopyMemory %6 %8 +OpReturn +OpFunctionEnd diff --git a/tests/ui/dis/unroll_loops.rs b/tests/ui/dis/unroll_loops.rs new file mode 100644 index 0000000000..d1d6a63e03 --- /dev/null +++ b/tests/ui/dis/unroll_loops.rs @@ -0,0 +1,18 @@ +// build-pass +// compile-flags: -C llvm-args=--disassemble-fn=unroll_loops::java_hash_ten_times + +use spirv_std as _; + +#[spirv(unroll_loops)] +fn java_hash_ten_times(mut x: u32, y: u32) -> u32 { + let mut i = 0; + while i < 10 { + x = 31 * x + y; + i += 1; + } + x +} +#[spirv(fragment)] +pub fn main() { + java_hash_ten_times(7, 42); +} diff --git a/tests/ui/dis/unroll_loops.stderr b/tests/ui/dis/unroll_loops.stderr new file mode 100644 index 0000000000..2be6206593 --- /dev/null +++ b/tests/ui/dis/unroll_loops.stderr @@ -0,0 +1,32 @@ +%1 = OpFunction %2 None %3 +%4 = OpFunctionParameter %2 +%5 = OpFunctionParameter %2 +%6 = OpLabel +OpBranch %7 +%7 = OpLabel +OpBranch %8 +%8 = OpLabel +%9 = OpPhi %10 %11 %7 %12 %13 +%14 = OpPhi %2 %4 %7 %15 %13 +%16 = OpPhi %17 %18 %7 %19 %13 +OpLoopMerge %20 %13 Unroll +OpBranchConditional %16 %21 %20 +%21 = OpLabel +%22 = OpSLessThan %17 %9 %23 +OpSelectionMerge %24 None +OpBranchConditional %22 %25 %26 +%25 = OpLabel +%27 = OpIMul %2 %28 %14 +%15 = OpIAdd %2 %27 %5 +%12 = OpIAdd %10 %9 %29 +OpBranch %24 +%26 = OpLabel +OpReturnValue %14 +%24 = OpLabel +%19 = OpPhi %17 %18 %25 +OpBranch %13 +%13 = OpLabel +OpBranch %8 +%20 = OpLabel +OpUnreachable +OpFunctionEnd