Add support for OpAtomicISub, OpAtomicIIncrement, OpAtomicIDecrement

These are the remaining operations which were not tested through the
dEQP-VK.image.* path.

Test: dEQP-VK.spirv_assembly.*
Bug: b/127472316
Change-Id: Icedc2fc7b972714bc67705e2458e1207972b95cc
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/29457
Tested-by: Chris Forbes <chrisforbes@google.com>
Presubmit-Ready: Chris Forbes <chrisforbes@google.com>
Kokoro-Presubmit: kokoro <noreply+kokoro@google.com>
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
diff --git a/src/Pipeline/SpirvShader.cpp b/src/Pipeline/SpirvShader.cpp
index 8312c71..43af987 100644
--- a/src/Pipeline/SpirvShader.cpp
+++ b/src/Pipeline/SpirvShader.cpp
@@ -679,6 +679,7 @@
 			case spv::OpFwidthFine:
 			case spv::OpAtomicLoad:
 			case spv::OpAtomicIAdd:
+			case spv::OpAtomicISub:
 			case spv::OpAtomicSMin:
 			case spv::OpAtomicSMax:
 			case spv::OpAtomicUMin:
@@ -686,6 +687,8 @@
 			case spv::OpAtomicAnd:
 			case spv::OpAtomicOr:
 			case spv::OpAtomicXor:
+			case spv::OpAtomicIIncrement:
+			case spv::OpAtomicIDecrement:
 			case spv::OpAtomicExchange:
 			case spv::OpAtomicCompareExchange:
 			case spv::OpPhi:
@@ -2017,6 +2020,7 @@
 			return EmitStore(insn, state);
 
 		case spv::OpAtomicIAdd:
+		case spv::OpAtomicISub:
 		case spv::OpAtomicSMin:
 		case spv::OpAtomicSMax:
 		case spv::OpAtomicUMin:
@@ -2024,6 +2028,8 @@
 		case spv::OpAtomicAnd:
 		case spv::OpAtomicOr:
 		case spv::OpAtomicXor:
+		case spv::OpAtomicIIncrement:
+		case spv::OpAtomicIDecrement:
 		case spv::OpAtomicExchange:
 			return EmitAtomicOp(insn, state);
 
@@ -4759,7 +4765,8 @@
 		Object::ID semanticsId = insn.word(5);
 		auto memorySemantics = static_cast<spv::MemorySemanticsMask>(getObject(semanticsId).constantValue[0]);
 		auto memoryOrder = MemoryOrder(memorySemantics);
-		auto value = GenericValue(this, state->routine, insn.word(6));
+		// Where no value is provided (increment/decrement) use an implicit value of 1.
+		auto value = (insn.wordCount() == 7) ? GenericValue(this, state->routine, insn.word(6)).UInt(0) : RValue<SIMD::UInt>(1);
 		auto &dst = state->routine->createIntermediate(resultId, resultType.sizeInComponents);
 		auto ptr = state->routine->getPointer(insn.word(3));
 
@@ -4769,13 +4776,18 @@
 			If(Extract(state->activeLaneMask(), j) != 0)
 			{
 				auto offset = Extract(ptr.offset, j);
-				auto laneValue = Extract(value.UInt(0), j);
+				auto laneValue = Extract(value, j);
 				UInt v;
 				switch (insn.opcode())
 				{
 				case spv::OpAtomicIAdd:
+				case spv::OpAtomicIIncrement:
 					v = AddAtomic(Pointer<UInt>(&ptr.base[offset]), laneValue, memoryOrder);
 					break;
+				case spv::OpAtomicISub:
+				case spv::OpAtomicIDecrement:
+					v = SubAtomic(Pointer<UInt>(&ptr.base[offset]), laneValue, memoryOrder);
+					break;
 				case spv::OpAtomicAnd:
 					v = AndAtomic(Pointer<UInt>(&ptr.base[offset]), laneValue, memoryOrder);
 					break;
diff --git a/src/Reactor/LLVMReactor.cpp b/src/Reactor/LLVMReactor.cpp
index 03720d6..c111d1a 100644
--- a/src/Reactor/LLVMReactor.cpp
+++ b/src/Reactor/LLVMReactor.cpp
@@ -1294,6 +1294,12 @@
 		return V(::builder->CreateAtomicRMW(llvm::AtomicRMWInst::Add, V(ptr), V(value), atomicOrdering(true, memoryOrder)));
 	}
 
+	Value *Nucleus::createAtomicSub(Value *ptr, Value *value, std::memory_order memoryOrder)
+	{
+		RR_DEBUG_INFO_UPDATE_LOC();
+		return V(::builder->CreateAtomicRMW(llvm::AtomicRMWInst::Sub, V(ptr), V(value), atomicOrdering(true, memoryOrder)));
+	}
+
 	Value *Nucleus::createAtomicAnd(Value *ptr, Value *value, std::memory_order memoryOrder)
 	{
 		RR_DEBUG_INFO_UPDATE_LOC();
diff --git a/src/Reactor/Nucleus.hpp b/src/Reactor/Nucleus.hpp
index 82ccafa..a524595 100644
--- a/src/Reactor/Nucleus.hpp
+++ b/src/Reactor/Nucleus.hpp
@@ -102,6 +102,7 @@
 
 		// Atomic instructions
 		static Value *createAtomicAdd(Value *ptr, Value *value, std::memory_order memoryOrder = std::memory_order_relaxed);
+		static Value *createAtomicSub(Value *ptr, Value *value, std::memory_order memoryOrder = std::memory_order_relaxed);
 		static Value *createAtomicAnd(Value *ptr, Value *value, std::memory_order memoryOrder = std::memory_order_relaxed);
 		static Value *createAtomicOr(Value *ptr, Value *value, std::memory_order memoryOrder = std::memory_order_relaxed);
 		static Value *createAtomicXor(Value *ptr, Value *value, std::memory_order memoryOrder = std::memory_order_relaxed);
diff --git a/src/Reactor/Reactor.cpp b/src/Reactor/Reactor.cpp
index 43bc1d6..b2c99e2 100644
--- a/src/Reactor/Reactor.cpp
+++ b/src/Reactor/Reactor.cpp
@@ -2512,6 +2512,11 @@
 		return RValue<UInt>(Nucleus::createAtomicAdd(x.value, y.value, memoryOrder));
 	}
 
+	RValue<UInt> SubAtomic(RValue<Pointer<UInt> > x, RValue<UInt> y, std::memory_order memoryOrder)
+	{
+		return RValue<UInt>(Nucleus::createAtomicSub(x.value, y.value, memoryOrder));
+	}
+
 	RValue<UInt> AndAtomic(RValue<Pointer<UInt> > x, RValue<UInt> y, std::memory_order memoryOrder)
 	{
 		return RValue<UInt>(Nucleus::createAtomicAnd(x.value, y.value, memoryOrder));
diff --git a/src/Reactor/Reactor.hpp b/src/Reactor/Reactor.hpp
index fc9a5a1..1c7b5ca 100644
--- a/src/Reactor/Reactor.hpp
+++ b/src/Reactor/Reactor.hpp
@@ -1269,6 +1269,7 @@
 	RValue<UInt> Clamp(RValue<UInt> x, RValue<UInt> min, RValue<UInt> max);
 
 	RValue<UInt> AddAtomic(RValue<Pointer<UInt>> x, RValue<UInt> y, std::memory_order memoryOrder);
+	RValue<UInt> SubAtomic(RValue<Pointer<UInt>> x, RValue<UInt> y, std::memory_order memoryOrder);
 	RValue<UInt> AndAtomic(RValue<Pointer<UInt>> x, RValue<UInt> y, std::memory_order memoryOrder);
 	RValue<UInt> OrAtomic(RValue<Pointer<UInt>> x, RValue<UInt> y, std::memory_order memoryOrder);
 	RValue<UInt> XorAtomic(RValue<Pointer<UInt>> x, RValue<UInt> y, std::memory_order memoryOrder);
diff --git a/src/Reactor/SubzeroReactor.cpp b/src/Reactor/SubzeroReactor.cpp
index b8a91db..86f0c00 100644
--- a/src/Reactor/SubzeroReactor.cpp
+++ b/src/Reactor/SubzeroReactor.cpp
@@ -1039,6 +1039,12 @@
 		return nullptr;
 	}
 
+	Value *Nucleus::createAtomicSub(Value *ptr, Value *value, std::memory_order memoryOrder)
+	{
+		UNIMPLEMENTED("createAtomicSub");
+		return nullptr;
+	}
+
 	Value *Nucleus::createAtomicAnd(Value *ptr, Value *value, std::memory_order memoryOrder)
 	{
 		UNIMPLEMENTED("createAtomicAnd");