mirror of
https://github.com/gfx-rs/wgpu.git
synced 2024-11-25 00:03:29 +00:00
spv-in parse more atomic ops (#5824)
* add parsing for spirv::Op::AtomicLoad and spirv::Op::AtomicStore * spv-in parse AtomicExchange and AtomicCompareExchange * add atomic i decrement * bookend atomic store statement with emmitter.finish/emitter.start to suppress a double load expression bookend atomic result expressions with emitter.finish/start to prevent double defs * add atomic iadd, isub, smin, umin, smax, umax, and, or, xor * parse atomic flag test and set, parse atomic flag clear * remove atomic compare exchange work * changelog * moved spirv tests into front/spv/mod.rs * feature gate atomic spv tests because they require wgsl-[in,out] * BlockContext::get_contained_global_variable returns Result * Generate spans covering the entire instruction. Granted, there is pre-existing code in the SPIR-V front end that gets this wrong, but: It doesn't make sense to read `self.data_offset`, and then immediately pass that to `self.span_from_with_op`. The point of that function is to make the span cover the entire instruction, operands included. * Move `From` implementation into spv front end * doc comments, minor cleanups * remove parsing of OpAtomicFlagClear and OpAtomicFlagTestAndSet * sync atomic spvasm files --------- Co-authored-by: Jim Blandy <jimb@red-bean.com>
This commit is contained in:
parent
434f197410
commit
fc85e4f970
@ -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
|
||||
|
||||
|
@ -44,12 +44,8 @@ pub enum Error {
|
||||
MultiMemberStruct,
|
||||
#[error("encountered unsupported global initializer in an atomic variable")]
|
||||
GlobalInitUnsupported,
|
||||
}
|
||||
|
||||
impl From<Error> 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)]
|
||||
|
@ -159,3 +159,9 @@ impl Error {
|
||||
String::from_utf8(writer.into_inner()).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<atomic_upgrade::Error> for Error {
|
||||
fn from(source: atomic_upgrade::Error) -> Self {
|
||||
crate::front::spv::Error::AtomicUpgradeError(source)
|
||||
}
|
||||
}
|
||||
|
@ -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<crate::Expression>,
|
||||
) -> Option<Handle<crate::GlobalVariable>> {
|
||||
) -> Result<Handle<crate::GlobalVariable>, 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<I: Iterator<Item = u32>> Frontend<I> {
|
||||
))
|
||||
}
|
||||
|
||||
/// 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<crate::Expression>, Handle<crate::Type>), 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<I: Iterator<Item = u32>> Frontend<I> {
|
||||
);
|
||||
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<I: Iterator<Item = u32>> Frontend<I> {
|
||||
);
|
||||
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<I: Iterator<Item = u32>> Frontend<I> {
|
||||
|
||||
// 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() {
|
||||
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(
|
||||
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())
|
||||
|
||||
#[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 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(),
|
||||
] {
|
||||
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!("atomic_i_increment:\n{wgsl}");
|
||||
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"));
|
||||
}
|
||||
}
|
||||
|
BIN
naga/tests/in/spv/atomic_exchange.spv
Normal file
BIN
naga/tests/in/spv/atomic_exchange.spv
Normal file
Binary file not shown.
88
naga/tests/in/spv/atomic_exchange.spvasm
Normal file
88
naga/tests/in/spv/atomic_exchange.spvasm
Normal file
@ -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
|
BIN
naga/tests/in/spv/atomic_i_add_sub.spv
Normal file
BIN
naga/tests/in/spv/atomic_i_add_sub.spv
Normal file
Binary file not shown.
51
naga/tests/in/spv/atomic_i_add_sub.spvasm
Normal file
51
naga/tests/in/spv/atomic_i_add_sub.spvasm
Normal file
@ -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
|
BIN
naga/tests/in/spv/atomic_i_decrement.spv
Normal file
BIN
naga/tests/in/spv/atomic_i_decrement.spv
Normal file
Binary file not shown.
64
naga/tests/in/spv/atomic_i_decrement.spvasm
Normal file
64
naga/tests/in/spv/atomic_i_decrement.spvasm
Normal file
@ -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
|
@ -59,4 +59,3 @@
|
||||
%26 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
|
||||
|
BIN
naga/tests/in/spv/atomic_load_and_store.spv
Normal file
BIN
naga/tests/in/spv/atomic_load_and_store.spv
Normal file
Binary file not shown.
86
naga/tests/in/spv/atomic_load_and_store.spvasm
Normal file
86
naga/tests/in/spv/atomic_load_and_store.spvasm
Normal file
@ -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
|
@ -216,10 +216,6 @@
|
||||
),
|
||||
],
|
||||
reject: [
|
||||
Emit((
|
||||
start: 13,
|
||||
end: 14,
|
||||
)),
|
||||
Atomic(
|
||||
pointer: 7,
|
||||
fun: Add,
|
||||
|
@ -241,10 +241,6 @@
|
||||
),
|
||||
],
|
||||
reject: [
|
||||
Emit((
|
||||
start: 14,
|
||||
end: 15,
|
||||
)),
|
||||
Atomic(
|
||||
pointer: 8,
|
||||
fun: Add,
|
||||
|
Loading…
Reference in New Issue
Block a user