diff --git a/CHANGELOG.md b/CHANGELOG.md index a647a0662..d42d72589 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -80,6 +80,7 @@ By @bradwerth [#6216](https://github.com/gfx-rs/wgpu/pull/6216). - Support constant evaluation for `firstLeadingBit` and `firstTrailingBit` numeric built-ins in WGSL. Front-ends that translate to these built-ins also benefit from constant evaluation. By @ErichDonGubler in [#5101](https://github.com/gfx-rs/wgpu/pull/5101). - Add `first` and `either` sampling types for `@interpolate(flat, …)` in WGSL. By @ErichDonGubler in [#6181](https://github.com/gfx-rs/wgpu/pull/6181). +- Support for more atomic ops in the SPIR-V frontend. By @schell in [#5824](https://github.com/gfx-rs/wgpu/pull/5824). #### Vulkan diff --git a/naga/src/front/atomic_upgrade.rs b/naga/src/front/atomic_upgrade.rs index c59969ace..d2fbc1289 100644 --- a/naga/src/front/atomic_upgrade.rs +++ b/naga/src/front/atomic_upgrade.rs @@ -44,12 +44,8 @@ pub enum Error { MultiMemberStruct, #[error("encountered unsupported global initializer in an atomic variable")] GlobalInitUnsupported, -} - -impl From for crate::front::spv::Error { - fn from(source: Error) -> Self { - crate::front::spv::Error::AtomicUpgradeError(source) - } + #[error("expected to find a global variable")] + GlobalVariableMissing, } #[derive(Clone, Default)] diff --git a/naga/src/front/spv/error.rs b/naga/src/front/spv/error.rs index 42df9d807..bcbb47dde 100644 --- a/naga/src/front/spv/error.rs +++ b/naga/src/front/spv/error.rs @@ -159,3 +159,9 @@ impl Error { String::from_utf8(writer.into_inner()).unwrap() } } + +impl From for Error { + fn from(source: atomic_upgrade::Error) -> Self { + crate::front::spv::Error::AtomicUpgradeError(source) + } +} diff --git a/naga/src/front/spv/mod.rs b/naga/src/front/spv/mod.rs index 7dfb4ae29..5ad063a6b 100644 --- a/naga/src/front/spv/mod.rs +++ b/naga/src/front/spv/mod.rs @@ -565,11 +565,15 @@ impl<'a> BlockContext<'a> { /// Descend into the expression with the given handle, locating a contained /// global variable. /// + /// If the expression doesn't actually refer to something in a global + /// variable, we can't upgrade its type in a way that Naga validation would + /// pass, so reject the input instead. + /// /// This is used to track atomic upgrades. fn get_contained_global_variable( &self, mut handle: Handle, - ) -> Option> { + ) -> Result, Error> { log::debug!("\t\tlocating global variable in {handle:?}"); loop { match self.expressions[handle] { @@ -583,14 +587,16 @@ impl<'a> BlockContext<'a> { } crate::Expression::GlobalVariable(h) => { log::debug!("\t\t found {h:?}"); - return Some(h); + return Ok(h); } _ => { break; } } } - None + Err(Error::AtomicUpgradeError( + crate::front::atomic_upgrade::Error::GlobalVariableMissing, + )) } } @@ -1323,6 +1329,109 @@ impl> Frontend { )) } + /// Return the Naga [`Expression`] for `pointer_id`, and its referent [`Type`]. + /// + /// Return a [`Handle`] for a Naga [`Expression`] that holds the value of + /// the SPIR-V instruction `pointer_id`, along with the [`Type`] to which it + /// is a pointer. + /// + /// This may entail spilling `pointer_id`'s value to a temporary: + /// see [`get_expr_handle`]'s documentation. + /// + /// [`Expression`]: crate::Expression + /// [`Type`]: crate::Type + /// [`Handle`]: crate::Handle + /// [`get_expr_handle`]: Frontend::get_expr_handle + fn get_exp_and_base_ty_handles( + &self, + pointer_id: spirv::Word, + ctx: &mut BlockContext, + emitter: &mut crate::proc::Emitter, + block: &mut crate::Block, + body_idx: usize, + ) -> Result<(Handle, Handle), Error> { + log::trace!("\t\t\tlooking up pointer expr {:?}", pointer_id); + let p_lexp_handle; + let p_lexp_ty_id; + { + let lexp = self.lookup_expression.lookup(pointer_id)?; + p_lexp_handle = self.get_expr_handle(pointer_id, lexp, ctx, emitter, block, body_idx); + p_lexp_ty_id = lexp.type_id; + }; + + log::trace!("\t\t\tlooking up pointer type {pointer_id:?}"); + let p_ty = self.lookup_type.lookup(p_lexp_ty_id)?; + let p_ty_base_id = p_ty.base_id.ok_or(Error::InvalidAccessType(p_lexp_ty_id))?; + + log::trace!("\t\t\tlooking up pointer base type {p_ty_base_id:?} of {p_ty:?}"); + let p_base_ty = self.lookup_type.lookup(p_ty_base_id)?; + + Ok((p_lexp_handle, p_base_ty.handle)) + } + + #[allow(clippy::too_many_arguments)] + fn parse_atomic_expr_with_value( + &mut self, + inst: Instruction, + emitter: &mut crate::proc::Emitter, + ctx: &mut BlockContext, + block: &mut crate::Block, + block_id: spirv::Word, + body_idx: usize, + atomic_function: crate::AtomicFunction, + ) -> Result<(), Error> { + inst.expect(7)?; + let start = self.data_offset; + let result_type_id = self.next()?; + let result_id = self.next()?; + let pointer_id = self.next()?; + let _scope_id = self.next()?; + let _memory_semantics_id = self.next()?; + let value_id = self.next()?; + let span = self.span_from_with_op(start); + + let (p_lexp_handle, p_base_ty_handle) = + self.get_exp_and_base_ty_handles(pointer_id, ctx, emitter, block, body_idx)?; + + log::trace!("\t\t\tlooking up value expr {value_id:?}"); + let v_lexp_handle = self.lookup_expression.lookup(value_id)?.handle; + + block.extend(emitter.finish(ctx.expressions)); + // Create an expression for our result + let r_lexp_handle = { + let expr = crate::Expression::AtomicResult { + ty: p_base_ty_handle, + comparison: false, + }; + let handle = ctx.expressions.append(expr, span); + self.lookup_expression.insert( + result_id, + LookupExpression { + handle, + type_id: result_type_id, + block_id, + }, + ); + handle + }; + emitter.start(ctx.expressions); + + // Create a statement for the op itself + let stmt = crate::Statement::Atomic { + pointer: p_lexp_handle, + fun: atomic_function, + value: v_lexp_handle, + result: Some(r_lexp_handle), + }; + block.push(stmt, span); + + // Store any associated global variables so we can upgrade their types later + self.upgrade_atomics + .insert(ctx.get_contained_global_variable(p_lexp_handle)?); + + Ok(()) + } + /// Add the next SPIR-V block's contents to `block_ctx`. /// /// Except for the function's entry block, `block_id` should be the label of @@ -3985,35 +4094,91 @@ impl> Frontend { ); emitter.start(ctx.expressions); } - Op::AtomicIIncrement => { + Op::AtomicLoad => { inst.expect(6)?; let start = self.data_offset; - let span = self.span_from_with_op(start); let result_type_id = self.next()?; let result_id = self.next()?; let pointer_id = self.next()?; let _scope_id = self.next()?; let _memory_semantics_id = self.next()?; + let span = self.span_from_with_op(start); log::trace!("\t\t\tlooking up expr {:?}", pointer_id); - let (p_lexp_handle, p_lexp_ty_id) = { - let lexp = self.lookup_expression.lookup(pointer_id)?; - let handle = get_expr_handle!(pointer_id, &lexp); - (handle, lexp.type_id) + let p_lexp_handle = + get_expr_handle!(pointer_id, self.lookup_expression.lookup(pointer_id)?); + + // Create an expression for our result + let expr = crate::Expression::Load { + pointer: p_lexp_handle, }; + let handle = ctx.expressions.append(expr, span); + self.lookup_expression.insert( + result_id, + LookupExpression { + handle, + type_id: result_type_id, + block_id, + }, + ); - log::trace!("\t\t\tlooking up type {pointer_id:?}"); - let p_ty = self.lookup_type.lookup(p_lexp_ty_id)?; - let p_ty_base_id = - p_ty.base_id.ok_or(Error::InvalidAccessType(p_lexp_ty_id))?; + // Store any associated global variables so we can upgrade their types later + self.upgrade_atomics + .insert(ctx.get_contained_global_variable(p_lexp_handle)?); + } + Op::AtomicStore => { + inst.expect(5)?; + let start = self.data_offset; + let pointer_id = self.next()?; + let _scope_id = self.next()?; + let _memory_semantics_id = self.next()?; + let value_id = self.next()?; + let span = self.span_from_with_op(start); - log::trace!("\t\t\tlooking up base type {p_ty_base_id:?} of {p_ty:?}"); - let p_base_ty = self.lookup_type.lookup(p_ty_base_id)?; + log::trace!("\t\t\tlooking up pointer expr {:?}", pointer_id); + let p_lexp_handle = + get_expr_handle!(pointer_id, self.lookup_expression.lookup(pointer_id)?); + log::trace!("\t\t\tlooking up value expr {:?}", pointer_id); + let v_lexp_handle = + get_expr_handle!(value_id, self.lookup_expression.lookup(value_id)?); + + block.extend(emitter.finish(ctx.expressions)); + // Create a statement for the op itself + let stmt = crate::Statement::Store { + pointer: p_lexp_handle, + value: v_lexp_handle, + }; + block.push(stmt, span); + emitter.start(ctx.expressions); + + // Store any associated global variables so we can upgrade their types later + self.upgrade_atomics + .insert(ctx.get_contained_global_variable(p_lexp_handle)?); + } + Op::AtomicIIncrement | Op::AtomicIDecrement => { + inst.expect(6)?; + let start = self.data_offset; + let result_type_id = self.next()?; + let result_id = self.next()?; + let pointer_id = self.next()?; + let _scope_id = self.next()?; + let _memory_semantics_id = self.next()?; + let span = self.span_from_with_op(start); + + let (p_exp_h, p_base_ty_h) = self.get_exp_and_base_ty_handles( + pointer_id, + ctx, + &mut emitter, + &mut block, + body_idx, + )?; + + block.extend(emitter.finish(ctx.expressions)); // Create an expression for our result let r_lexp_handle = { let expr = crate::Expression::AtomicResult { - ty: p_base_ty.handle, + ty: p_base_ty_h, comparison: false, }; let handle = ctx.expressions.append(expr, span); @@ -4027,22 +4192,26 @@ impl> Frontend { ); handle }; + emitter.start(ctx.expressions); - // Create a literal "1" since WGSL lacks an increment operation + // Create a literal "1" to use as our value let one_lexp_handle = make_index_literal( ctx, 1, &mut block, &mut emitter, - p_base_ty.handle, - p_lexp_ty_id, + p_base_ty_h, + result_type_id, span, )?; // Create a statement for the op itself let stmt = crate::Statement::Atomic { - pointer: p_lexp_handle, - fun: crate::AtomicFunction::Add, + pointer: p_exp_h, + fun: match inst.op { + Op::AtomicIIncrement => crate::AtomicFunction::Add, + _ => crate::AtomicFunction::Subtract, + }, value: one_lexp_handle, result: Some(r_lexp_handle), }; @@ -4050,8 +4219,38 @@ impl> Frontend { // Store any associated global variables so we can upgrade their types later self.upgrade_atomics - .extend(ctx.get_contained_global_variable(p_lexp_handle)); + .insert(ctx.get_contained_global_variable(p_exp_h)?); } + Op::AtomicExchange + | Op::AtomicIAdd + | Op::AtomicISub + | Op::AtomicSMin + | Op::AtomicUMin + | Op::AtomicSMax + | Op::AtomicUMax + | Op::AtomicAnd + | Op::AtomicOr + | Op::AtomicXor => self.parse_atomic_expr_with_value( + inst, + &mut emitter, + ctx, + &mut block, + block_id, + body_idx, + match inst.op { + Op::AtomicExchange => crate::AtomicFunction::Exchange { compare: None }, + Op::AtomicIAdd => crate::AtomicFunction::Add, + Op::AtomicISub => crate::AtomicFunction::Subtract, + Op::AtomicSMin => crate::AtomicFunction::Min, + Op::AtomicUMin => crate::AtomicFunction::Min, + Op::AtomicSMax => crate::AtomicFunction::Max, + Op::AtomicUMax => crate::AtomicFunction::Max, + Op::AtomicAnd => crate::AtomicFunction::And, + Op::AtomicOr => crate::AtomicFunction::InclusiveOr, + _ => crate::AtomicFunction::ExclusiveOr, + }, + )?, + _ => { return Err(Error::UnsupportedInstruction(self.state, inst.op)); } @@ -5709,33 +5908,48 @@ mod test { ]; let _ = super::parse_u8_slice(&bin, &Default::default()).unwrap(); } +} - #[cfg(all(feature = "wgsl-in", wgsl_out))] - #[test] - fn atomic_i_inc() { +#[cfg(all(test, feature = "wgsl-in", wgsl_out))] +mod test_atomic { + fn atomic_test(bytes: &[u8]) { let _ = env_logger::builder().is_test(true).try_init(); - let bytes = include_bytes!("../../../tests/in/spv/atomic_i_increment.spv"); - let m = super::parse_u8_slice(bytes, &Default::default()).unwrap(); - let mut validator = crate::valid::Validator::new( + let m = crate::front::spv::parse_u8_slice(bytes, &Default::default()).unwrap(); + + let mut wgsl = String::new(); + let mut should_panic = false; + + for vflags in [ + crate::valid::ValidationFlags::all(), crate::valid::ValidationFlags::empty(), - Default::default(), - ); - let info = match validator.validate(&m) { - Err(e) => { - log::error!("{}", e.emit_to_string("")); - return; - } - Ok(i) => i, - }; - let wgsl = - crate::back::wgsl::write_string(&m, &info, crate::back::wgsl::WriterFlags::empty()) - .unwrap(); - log::info!("atomic_i_increment:\n{wgsl}"); + ] { + let mut validator = crate::valid::Validator::new(vflags, Default::default()); + match validator.validate(&m) { + Err(e) => { + log::error!("SPIR-V validation {}", e.emit_to_string("")); + should_panic = true; + } + Ok(i) => { + wgsl = crate::back::wgsl::write_string( + &m, + &i, + crate::back::wgsl::WriterFlags::empty(), + ) + .unwrap(); + log::info!("wgsl-out:\n{wgsl}"); + break; + } + }; + } + + if should_panic { + panic!("validation error"); + } let m = match crate::front::wgsl::parse_str(&wgsl) { Ok(m) => m, Err(e) => { - log::error!("{}", e.emit_to_string(&wgsl)); + log::error!("round trip WGSL validation {}", e.emit_to_string(&wgsl)); panic!("invalid module"); } }; @@ -5746,4 +5960,35 @@ mod test { panic!("invalid generated wgsl"); } } + + #[test] + fn atomic_i_inc() { + atomic_test(include_bytes!( + "../../../tests/in/spv/atomic_i_increment.spv" + )); + } + + #[test] + fn atomic_load_and_store() { + atomic_test(include_bytes!( + "../../../tests/in/spv/atomic_load_and_store.spv" + )); + } + + #[test] + fn atomic_exchange() { + atomic_test(include_bytes!("../../../tests/in/spv/atomic_exchange.spv")); + } + + #[test] + fn atomic_i_decrement() { + atomic_test(include_bytes!( + "../../../tests/in/spv/atomic_i_decrement.spv" + )); + } + + #[test] + fn atomic_i_add_and_sub() { + atomic_test(include_bytes!("../../../tests/in/spv/atomic_i_add_sub.spv")); + } } diff --git a/naga/tests/in/spv/atomic_exchange.spv b/naga/tests/in/spv/atomic_exchange.spv new file mode 100644 index 000000000..cc64ce9aa Binary files /dev/null and b/naga/tests/in/spv/atomic_exchange.spv differ diff --git a/naga/tests/in/spv/atomic_exchange.spvasm b/naga/tests/in/spv/atomic_exchange.spvasm new file mode 100644 index 000000000..09258f058 --- /dev/null +++ b/naga/tests/in/spv/atomic_exchange.spvasm @@ -0,0 +1,88 @@ +; SPIR-V +; Version: 1.5 +; Generator: Google rspirv; 0 +; Bound: 63 +; Schema: 0 + OpCapability Shader + OpCapability VulkanMemoryModel + OpMemoryModel Logical Vulkan + OpEntryPoint GLCompute %1 "stage::test_atomic_exchange" %2 %3 + OpExecutionMode %1 LocalSize 32 1 1 + OpMemberDecorate %_struct_11 0 Offset 0 + OpMemberDecorate %_struct_11 1 Offset 4 + OpDecorate %_struct_12 Block + OpMemberDecorate %_struct_12 0 Offset 0 + OpDecorate %2 Binding 0 + OpDecorate %2 DescriptorSet 0 + OpDecorate %3 NonWritable + OpDecorate %3 Binding 1 + OpDecorate %3 DescriptorSet 0 + %uint = OpTypeInt 32 0 + %void = OpTypeVoid + %15 = OpTypeFunction %void + %bool = OpTypeBool + %uint_0 = OpConstant %uint 0 + %uint_2 = OpConstant %uint 2 + %false = OpConstantFalse %bool +%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint + %uint_1 = OpConstant %uint 1 + %_struct_11 = OpTypeStruct %uint %uint + %22 = OpUndef %_struct_11 + %int = OpTypeInt 32 1 + %true = OpConstantTrue %bool + %_struct_12 = OpTypeStruct %uint +%_ptr_StorageBuffer__struct_12 = OpTypePointer StorageBuffer %_struct_12 + %2 = OpVariable %_ptr_StorageBuffer__struct_12 StorageBuffer + %3 = OpVariable %_ptr_StorageBuffer__struct_12 StorageBuffer + %26 = OpUndef %uint + %1 = OpFunction %void None %15 + %27 = OpLabel + %28 = OpAccessChain %_ptr_StorageBuffer_uint %2 %uint_0 + %29 = OpAccessChain %_ptr_StorageBuffer_uint %3 %uint_0 + %30 = OpLoad %uint %29 + %31 = OpCompositeConstruct %_struct_11 %uint_0 %30 + OpBranch %32 + %32 = OpLabel + %33 = OpPhi %_struct_11 %31 %27 %34 %35 + %36 = OpPhi %uint %uint_0 %27 %37 %35 + OpLoopMerge %38 %35 None + OpBranch %39 + %39 = OpLabel + %40 = OpCompositeExtract %uint %33 0 + %41 = OpCompositeExtract %uint %33 1 + %42 = OpULessThan %bool %40 %41 + OpSelectionMerge %43 None + OpBranchConditional %42 %44 %45 + %44 = OpLabel + %47 = OpIAdd %uint %40 %uint_1 + %49 = OpCompositeInsert %_struct_11 %47 %33 0 + %50 = OpCompositeConstruct %_struct_11 %uint_1 %40 + OpBranch %43 + %45 = OpLabel + %51 = OpCompositeInsert %_struct_11 %uint_0 %22 0 + OpBranch %43 + %43 = OpLabel + %52 = OpPhi %_struct_11 %49 %44 %33 %45 + %53 = OpPhi %_struct_11 %50 %44 %51 %45 + %54 = OpCompositeExtract %uint %53 0 + %55 = OpBitcast %int %54 + OpSelectionMerge %56 None + OpSwitch %55 %57 0 %58 1 %59 + %57 = OpLabel + OpBranch %56 + %58 = OpLabel + OpBranch %56 + %59 = OpLabel + %60 = OpAtomicExchange %uint %28 %uint_2 %uint_0 %36 + %61 = OpIAdd %uint %36 %60 + OpBranch %56 + %56 = OpLabel + %62 = OpPhi %bool %false %57 %false %58 %true %59 + %34 = OpPhi %_struct_11 %22 %57 %22 %58 %52 %59 + %37 = OpPhi %uint %26 %57 %26 %58 %61 %59 + OpBranch %35 + %35 = OpLabel + OpBranchConditional %62 %32 %38 + %38 = OpLabel + OpReturn + OpFunctionEnd diff --git a/naga/tests/in/spv/atomic_i_add_sub.spv b/naga/tests/in/spv/atomic_i_add_sub.spv new file mode 100644 index 000000000..8c2685040 Binary files /dev/null and b/naga/tests/in/spv/atomic_i_add_sub.spv differ diff --git a/naga/tests/in/spv/atomic_i_add_sub.spvasm b/naga/tests/in/spv/atomic_i_add_sub.spvasm new file mode 100644 index 000000000..b23af9958 --- /dev/null +++ b/naga/tests/in/spv/atomic_i_add_sub.spvasm @@ -0,0 +1,51 @@ +; SPIR-V +; Version: 1.5 +; Generator: Google rspirv; 0 +; Bound: 30 +; Schema: 0 + OpCapability Shader + OpCapability VulkanMemoryModel + OpMemoryModel Logical Vulkan + OpEntryPoint GLCompute %1 "stage::test_atomic_i_add_sub" %2 %3 + OpExecutionMode %1 LocalSize 32 1 1 + OpDecorate %_runtimearr_uint ArrayStride 4 + OpDecorate %_struct_7 Block + OpMemberDecorate %_struct_7 0 Offset 0 + OpDecorate %_struct_8 Block + OpMemberDecorate %_struct_8 0 Offset 0 + OpDecorate %2 Binding 0 + OpDecorate %2 DescriptorSet 0 + OpDecorate %3 Binding 1 + OpDecorate %3 DescriptorSet 0 + %uint = OpTypeInt 32 0 + %void = OpTypeVoid + %11 = OpTypeFunction %void + %bool = OpTypeBool +%_runtimearr_uint = OpTypeRuntimeArray %uint + %_struct_7 = OpTypeStruct %_runtimearr_uint +%_ptr_StorageBuffer__struct_7 = OpTypePointer StorageBuffer %_struct_7 + %uint_0 = OpConstant %uint 0 + %uint_2 = OpConstant %uint 2 +%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint + %_struct_8 = OpTypeStruct %uint +%_ptr_StorageBuffer__struct_8 = OpTypePointer StorageBuffer %_struct_8 + %2 = OpVariable %_ptr_StorageBuffer__struct_8 StorageBuffer + %3 = OpVariable %_ptr_StorageBuffer__struct_7 StorageBuffer + %1 = OpFunction %void None %11 + %19 = OpLabel + %20 = OpAccessChain %_ptr_StorageBuffer_uint %2 %uint_0 + %22 = OpArrayLength %uint %3 0 + %23 = OpAtomicIAdd %uint %20 %uint_2 %uint_0 %uint_2 + %24 = OpAtomicISub %uint %20 %uint_2 %uint_0 %23 + %25 = OpULessThan %bool %23 %22 + OpSelectionMerge %26 None + OpBranchConditional %25 %27 %28 + %27 = OpLabel + %29 = OpAccessChain %_ptr_StorageBuffer_uint %3 %uint_0 %23 + OpStore %29 %24 + OpBranch %26 + %28 = OpLabel + OpBranch %26 + %26 = OpLabel + OpReturn + OpFunctionEnd diff --git a/naga/tests/in/spv/atomic_i_decrement.spv b/naga/tests/in/spv/atomic_i_decrement.spv new file mode 100644 index 000000000..fda602ab5 Binary files /dev/null and b/naga/tests/in/spv/atomic_i_decrement.spv differ diff --git a/naga/tests/in/spv/atomic_i_decrement.spvasm b/naga/tests/in/spv/atomic_i_decrement.spvasm new file mode 100644 index 000000000..cc125beec --- /dev/null +++ b/naga/tests/in/spv/atomic_i_decrement.spvasm @@ -0,0 +1,64 @@ +; SPIR-V +; Version: 1.5 +; Generator: Google rspirv; 0 +; Bound: 42 +; Schema: 0 + OpCapability Shader + OpCapability VulkanMemoryModel + OpMemoryModel Logical Vulkan + OpEntryPoint GLCompute %1 "stage::test_atomic_i_decrement" %2 %3 + OpExecutionMode %1 LocalSize 32 1 1 + OpDecorate %_runtimearr_uint ArrayStride 4 + OpDecorate %_struct_7 Block + OpMemberDecorate %_struct_7 0 Offset 0 + OpDecorate %_struct_8 Block + OpMemberDecorate %_struct_8 0 Offset 0 + OpDecorate %2 Binding 0 + OpDecorate %2 DescriptorSet 0 + OpDecorate %3 Binding 1 + OpDecorate %3 DescriptorSet 0 + %uint = OpTypeInt 32 0 + %void = OpTypeVoid + %11 = OpTypeFunction %void + %bool = OpTypeBool +%_runtimearr_uint = OpTypeRuntimeArray %uint + %_struct_7 = OpTypeStruct %_runtimearr_uint +%_ptr_StorageBuffer__struct_7 = OpTypePointer StorageBuffer %_struct_7 + %uint_0 = OpConstant %uint 0 + %uint_2 = OpConstant %uint 2 + %false = OpConstantFalse %bool +%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint + %true = OpConstantTrue %bool + %_struct_8 = OpTypeStruct %uint +%_ptr_StorageBuffer__struct_8 = OpTypePointer StorageBuffer %_struct_8 + %2 = OpVariable %_ptr_StorageBuffer__struct_8 StorageBuffer + %3 = OpVariable %_ptr_StorageBuffer__struct_7 StorageBuffer + %1 = OpFunction %void None %11 + %21 = OpLabel + %22 = OpAccessChain %_ptr_StorageBuffer_uint %2 %uint_0 + %24 = OpArrayLength %uint %3 0 + OpBranch %25 + %25 = OpLabel + OpLoopMerge %26 %27 None + OpBranch %28 + %28 = OpLabel + %29 = OpAtomicIDecrement %uint %22 %uint_2 %uint_0 + %30 = OpULessThan %bool %29 %24 + OpSelectionMerge %31 None + OpBranchConditional %30 %32 %33 + %32 = OpLabel + %34 = OpAccessChain %_ptr_StorageBuffer_uint %3 %uint_0 %29 + OpStore %34 %29 + %35 = OpIEqual %bool %29 %uint_0 + %41 = OpSelect %bool %35 %false %true + OpBranch %31 + %33 = OpLabel + OpBranch %31 + %31 = OpLabel + %40 = OpPhi %bool %41 %32 %false %33 + OpBranch %27 + %27 = OpLabel + OpBranchConditional %40 %25 %26 + %26 = OpLabel + OpReturn + OpFunctionEnd diff --git a/naga/tests/in/spv/atomic_i_increment.spvasm b/naga/tests/in/spv/atomic_i_increment.spvasm index 4586102d2..c072ca298 100644 --- a/naga/tests/in/spv/atomic_i_increment.spvasm +++ b/naga/tests/in/spv/atomic_i_increment.spvasm @@ -59,4 +59,3 @@ %26 = OpLabel OpReturn OpFunctionEnd - diff --git a/naga/tests/in/spv/atomic_load_and_store.spv b/naga/tests/in/spv/atomic_load_and_store.spv new file mode 100644 index 000000000..e2e9ddfd0 Binary files /dev/null and b/naga/tests/in/spv/atomic_load_and_store.spv differ diff --git a/naga/tests/in/spv/atomic_load_and_store.spvasm b/naga/tests/in/spv/atomic_load_and_store.spvasm new file mode 100644 index 000000000..f65600c43 --- /dev/null +++ b/naga/tests/in/spv/atomic_load_and_store.spvasm @@ -0,0 +1,86 @@ +; SPIR-V +; Version: 1.5 +; Generator: Google rspirv; 0 +; Bound: 60 +; Schema: 0 + OpCapability Shader + OpCapability VulkanMemoryModel + OpMemoryModel Logical Vulkan + OpEntryPoint GLCompute %1 "stage::test_atomic_load_and_store" %2 %3 + OpExecutionMode %1 LocalSize 32 1 1 + OpMemberDecorate %_struct_11 0 Offset 0 + OpMemberDecorate %_struct_11 1 Offset 4 + OpDecorate %_struct_12 Block + OpMemberDecorate %_struct_12 0 Offset 0 + OpDecorate %2 Binding 0 + OpDecorate %2 DescriptorSet 0 + OpDecorate %3 NonWritable + OpDecorate %3 Binding 1 + OpDecorate %3 DescriptorSet 0 + %uint = OpTypeInt 32 0 + %void = OpTypeVoid + %15 = OpTypeFunction %void + %bool = OpTypeBool + %uint_0 = OpConstant %uint 0 + %uint_2 = OpConstant %uint 2 + %false = OpConstantFalse %bool +%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint + %uint_1 = OpConstant %uint 1 + %_struct_11 = OpTypeStruct %uint %uint + %22 = OpUndef %_struct_11 + %int = OpTypeInt 32 1 + %true = OpConstantTrue %bool + %_struct_12 = OpTypeStruct %uint +%_ptr_StorageBuffer__struct_12 = OpTypePointer StorageBuffer %_struct_12 + %2 = OpVariable %_ptr_StorageBuffer__struct_12 StorageBuffer + %3 = OpVariable %_ptr_StorageBuffer__struct_12 StorageBuffer + %1 = OpFunction %void None %15 + %26 = OpLabel + %27 = OpAccessChain %_ptr_StorageBuffer_uint %2 %uint_0 + %28 = OpAccessChain %_ptr_StorageBuffer_uint %3 %uint_0 + %29 = OpLoad %uint %28 + %30 = OpCompositeConstruct %_struct_11 %uint_0 %29 + OpBranch %31 + %31 = OpLabel + %32 = OpPhi %_struct_11 %30 %26 %33 %34 + OpLoopMerge %35 %34 None + OpBranch %36 + %36 = OpLabel + %37 = OpCompositeExtract %uint %32 0 + %38 = OpCompositeExtract %uint %32 1 + %39 = OpULessThan %bool %37 %38 + OpSelectionMerge %40 None + OpBranchConditional %39 %41 %42 + %41 = OpLabel + %44 = OpIAdd %uint %37 %uint_1 + %46 = OpCompositeInsert %_struct_11 %44 %32 0 + %47 = OpCompositeConstruct %_struct_11 %uint_1 %37 + OpBranch %40 + %42 = OpLabel + %48 = OpCompositeInsert %_struct_11 %uint_0 %22 0 + OpBranch %40 + %40 = OpLabel + %49 = OpPhi %_struct_11 %46 %41 %32 %42 + %50 = OpPhi %_struct_11 %47 %41 %48 %42 + %51 = OpCompositeExtract %uint %50 0 + %52 = OpBitcast %int %51 + OpSelectionMerge %53 None + OpSwitch %52 %54 0 %55 1 %56 + %54 = OpLabel + OpBranch %53 + %55 = OpLabel + OpBranch %53 + %56 = OpLabel + %57 = OpAtomicLoad %uint %27 %uint_2 %uint_0 + %58 = OpIAdd %uint %57 %uint_2 + OpAtomicStore %27 %uint_2 %uint_0 %58 + OpBranch %53 + %53 = OpLabel + %59 = OpPhi %bool %false %54 %false %55 %true %56 + %33 = OpPhi %_struct_11 %22 %54 %22 %55 %49 %56 + OpBranch %34 + %34 = OpLabel + OpBranchConditional %59 %31 %35 + %35 = OpLabel + OpReturn + OpFunctionEnd diff --git a/naga/tests/out/ir/atomic_i_increment.compact.ron b/naga/tests/out/ir/atomic_i_increment.compact.ron index 58b01f587..12a4692a3 100644 --- a/naga/tests/out/ir/atomic_i_increment.compact.ron +++ b/naga/tests/out/ir/atomic_i_increment.compact.ron @@ -216,10 +216,6 @@ ), ], reject: [ - Emit(( - start: 13, - end: 14, - )), Atomic( pointer: 7, fun: Add, diff --git a/naga/tests/out/ir/atomic_i_increment.ron b/naga/tests/out/ir/atomic_i_increment.ron index 2c5528921..82fa97502 100644 --- a/naga/tests/out/ir/atomic_i_increment.ron +++ b/naga/tests/out/ir/atomic_i_increment.ron @@ -241,10 +241,6 @@ ), ], reject: [ - Emit(( - start: 14, - end: 15, - )), Atomic( pointer: 8, fun: Add,