[spirv] Invalid codegen in RWByteAddressBuffer InterlockedMin and Max#5707
[spirv] Invalid codegen in RWByteAddressBuffer InterlockedMin and Max#5707Jasper-Bekkers wants to merge 2 commits intomicrosoft:mainfrom
Conversation
|
Iteration times made it such that I couldn't test this until now: this change doesn't work (yet). |
|
You will want to add a unit test. That could help with your turn around time too. See examples in https://github.com/microsoft/DirectXShaderCompiler/tree/main/tools/clang/test/CodeGenSPIRV_Lit. The can be run by building the target |
|
I actually think the fix I did is not correct, but I lack the expertise to make it correct. An alternative way to address this I think would be to do a twos complement flip for negative numbers before feeding them into the u32 atomics. |
| spvBuilder.getConstantInt(astContext.UnsignedIntTy, llvm::APInt(32, 0)); | ||
| auto *offset = doExpr(expr->getArg(0)); | ||
| const auto *dest = expr->getArg(0); | ||
| const auto baseType = dest->getType()->getCanonicalTypeUnqualified(); |
There was a problem hiding this comment.
Looking at the DXIL generated for these cases, the type of the operation is determined by the type of value, not the type of dest, which is actually an offset.
| SpirvInstruction *originalVal = spvBuilder.createAtomicOp( | ||
| translateAtomicHlslOpcodeToSpirvOpcode(opcode), | ||
| astContext.UnsignedIntTy, ptr, spv::Scope::Device, | ||
| atomicOp, baseType, ptr, spv::Scope::Device, |
There was a problem hiding this comment.
The problem is that the type for baseType does not match the type for value. That causes a validation error. You cannot cast the pointer to another type, but you could cast the input type to baseType, when they differ, and are the same number of bits. Do not change the opcode. The opcode seem correct as is.
There was a problem hiding this comment.
Take this example: https://godbolt.org/z/3sz6MEx9s.
It produces:
%39 = OpAccessChain %_ptr_StorageBuffer_uint %buffer %uint_0 %37
%40 = OpLoad %int %inputValue
%41 = OpAtomicSMax %uint %39 %uint_1 %uint_0 %40
This is invalid because the pointer %39 points to a uint, and the value %40 is %int. However you can make it valid by changing it to:
%39 = OpAccessChain %_ptr_StorageBuffer_uint %buffer %uint_0 %37
%40 = OpLoad %int %inputValue
%bc = OpBitcast %uint %40
%41 = OpAtomicSMax %uint %39 %uint_1 %uint_0 %bc
| if (atomicOp == spv::Op::OpAtomicUMax && baseType->isSignedIntegerType()) | ||
| atomicOp = spv::Op::OpAtomicSMax; | ||
| if (atomicOp == spv::Op::OpAtomicSMax && baseType->isUnsignedIntegerType()) | ||
| atomicOp = spv::Op::OpAtomicUMax; | ||
| if (atomicOp == spv::Op::OpAtomicUMin && baseType->isSignedIntegerType()) | ||
| atomicOp = spv::Op::OpAtomicSMin; | ||
| if (atomicOp == spv::Op::OpAtomicSMin && baseType->isUnsignedIntegerType()) | ||
| atomicOp = spv::Op::OpAtomicUMin; |
There was a problem hiding this comment.
These changes are not needed. I believe this will give incorrect results. The parser already determine the correct opcode.
|
@Jasper-Bekkers Are you still looking to address this PR or should we opt to close it at this point? |
Probably alright, I don't have the bandwidth to look into this anymore from our side but the bug still remains unfortunately. |
RWByteAddressBuffer has overloads for InterlockedMin and InterlockedMax for signed ints that were failing to compile due to mismatched types in the generated SPIR-V instruction. This adds the missing cast if necessary. At the same time, some redundant code is removed from the InterlockedMin/Max intrinsic non-member functions' codegen to modify the opcode. If it was necessary in the past, the frontend has since been fixed and it is no longer necessary. Tests to verify these combinations and the necessary implicit casts have also been added. Fixes microsoft#3196 Related to microsoft#4189, microsoft#6254, microsoft#5707
RWByteAddressBuffer has overloads for InterlockedMin and InterlockedMax for signed ints that were failing to compile due to mismatched types in the generated SPIR-V instruction. This adds the missing cast if necessary. At the same time, some redundant code is removed from the InterlockedMin/Max intrinsic non-member functions' codegen to modify the opcode. If it was necessary in the past, the frontend has since been fixed and it is no longer necessary. Tests to verify these combinations and the necessary implicit casts have also been added. Fixes #3196 Related to #4189, #6254, #5707
RWByteAddressBuffer has overloads for InterlockedMin and InterlockedMax for signed ints that were failing to compile due to mismatched types in the generated SPIR-V instruction. This adds the missing cast if necessary. At the same time, some redundant code is removed from the InterlockedMin/Max intrinsic non-member functions' codegen to modify the opcode. If it was necessary in the past, the frontend has since been fixed and it is no longer necessary. Tests to verify these combinations and the necessary implicit casts have also been added. Fixes microsoft#3196 Related to microsoft#4189, microsoft#6254, microsoft#5707
RWByteAddressBuffer has overloads for InterlockedMin and InterlockedMax for signed ints that fail to compile when used from SPIR-V.
Obviously we can't return a %uint from a signed atomic operation, however in
processRWByteAddressBufferAtomicMethodsthe call was always assumed to take %uint's.This PR attempt to resolve that in the same way as for other Interlocked operations by using the result expected type instead.
Repo cases