SpirvShader: Implement OpCopyMemory

Tests: dEQP-VK.spirv_assembly.instruction.compute.memory_access.*
Change-Id: I5281798468651c67ec038bdd8688f99c67654cb1
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/30215
Tested-by: Ben Clayton <bclayton@google.com>
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
diff --git a/src/Pipeline/SpirvShader.cpp b/src/Pipeline/SpirvShader.cpp
index b429df7..0ed371a 100644
--- a/src/Pipeline/SpirvShader.cpp
+++ b/src/Pipeline/SpirvShader.cpp
@@ -857,6 +857,7 @@
 			case spv::OpStore:
 			case spv::OpAtomicStore:
 			case spv::OpImageWrite:
+			case spv::OpCopyMemory:
 				// Don't need to do anything during analysis pass
 				break;
 
@@ -2432,6 +2433,9 @@
 		case spv::OpImage:
 			return EmitSampledImageCombineOrSplit(insn, state);
 
+		case spv::OpCopyMemory:
+			return EmitCopyMemory(insn, state);
+
 		default:
 			UNIMPLEMENTED("%s", OpcodeName(opcode).c_str());
 			break;
@@ -5248,6 +5252,38 @@
 		return EmitResult::Continue;
 	}
 
+	SpirvShader::EmitResult SpirvShader::EmitCopyMemory(InsnIterator insn, EmitState *state) const
+	{
+		Object::ID dstPtrId = insn.word(1);
+		Object::ID srcPtrId = insn.word(2);
+		auto &dstPtrTy = getType(getObject(dstPtrId).type);
+		auto &srcPtrTy = getType(getObject(srcPtrId).type);
+		ASSERT(dstPtrTy.element == srcPtrTy.element);
+
+		bool dstInterleavedByLane = IsStorageInterleavedByLane(dstPtrTy.storageClass);
+		bool srcInterleavedByLane = IsStorageInterleavedByLane(srcPtrTy.storageClass);
+		auto dstPtr = state->routine->getPointer(dstPtrId);
+		auto srcPtr = state->routine->getPointer(srcPtrId);
+
+		std::unordered_map<uint32_t, uint32_t> srcOffsets;
+
+		VisitMemoryObject(srcPtrId, [&](uint32_t i, uint32_t srcOffset) { srcOffsets[i] = srcOffset; });
+
+		VisitMemoryObject(dstPtrId, [&](uint32_t i, uint32_t dstOffset)
+		{
+			auto it = srcOffsets.find(i);
+			ASSERT(it != srcOffsets.end());
+			auto srcOffset = it->second;
+
+			auto dst = dstPtr + dstOffset;
+			auto src = srcPtr + srcOffset;
+			if (dstInterleavedByLane) { dst = interleaveByLane(dst); }
+			if (srcInterleavedByLane) { src = interleaveByLane(src); }
+			SIMD::Store(dst, SIMD::Load<SIMD::Float>(src, state->activeLaneMask()), state->activeLaneMask());
+		});
+		return EmitResult::Continue;
+	}
+
 	void SpirvShader::emitEpilog(SpirvRoutine *routine) const
 	{
 		for (auto insn : *this)
diff --git a/src/Pipeline/SpirvShader.hpp b/src/Pipeline/SpirvShader.hpp
index 1d09f88..29372f3 100644
--- a/src/Pipeline/SpirvShader.hpp
+++ b/src/Pipeline/SpirvShader.hpp
@@ -905,6 +905,7 @@
 		EmitResult EmitAtomicOp(InsnIterator insn, EmitState *state) const;
 		EmitResult EmitAtomicCompareExchange(InsnIterator insn, EmitState *state) const;
 		EmitResult EmitSampledImageCombineOrSplit(InsnIterator insn, EmitState *state) const;
+		EmitResult EmitCopyMemory(InsnIterator insn, EmitState *state) const;
 
 		SIMD::Pointer GetTexelAddress(SpirvRoutine const * routine, SIMD::Pointer base, GenericValue const & coordinate, Type const & imageType, Pointer<Byte> descriptor, int texelSize) const;