SpirvShader: Implement OpArrayLength

Tests: dEQP-VK.ssbo.unsized_array_length.*
Bug: b/132336662
Change-Id: I122788b1096795ff32b87418e2d65cbe1641af33
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/31010
Tested-by: Ben Clayton <bclayton@google.com>
Reviewed-by: Chris Forbes <chrisforbes@google.com>
diff --git a/src/Pipeline/SpirvShader.cpp b/src/Pipeline/SpirvShader.cpp
index 423706d..4ffa5e4 100644
--- a/src/Pipeline/SpirvShader.cpp
+++ b/src/Pipeline/SpirvShader.cpp
@@ -876,6 +876,7 @@
 			case spv::OpImageTexelPointer:
 			case spv::OpGroupNonUniformElect:
 			case spv::OpCopyObject:
+			case spv::OpArrayLength:
 				// Instructions that yield an intermediate value or divergent pointer
 				DefineResult(insn);
 				break;
@@ -2484,6 +2485,9 @@
 		case spv::OpGroupNonUniformElect:
 			return EmitGroupNonUniform(insn, state);
 
+		case spv::OpArrayLength:
+			return EmitArrayLength(insn, state);
+
 		default:
 			UNREACHABLE("%s", OpcodeName(opcode).c_str());
 			break;
@@ -5579,6 +5583,39 @@
 		return EmitResult::Continue;
 	}
 
+	SpirvShader::EmitResult SpirvShader::EmitArrayLength(InsnIterator insn, EmitState *state) const
+	{
+		auto resultTyId = Type::ID(insn.word(1));
+		auto resultId = Object::ID(insn.word(2));
+		auto structPtrId = Object::ID(insn.word(3));
+		auto arrayFieldIdx = insn.word(4);
+
+		auto &resultType = getType(resultTyId);
+		ASSERT(resultType.sizeInComponents == 1);
+		ASSERT(resultType.definition.opcode() == spv::OpTypeInt);
+
+		auto &structPtrTy = getType(getObject(structPtrId).type);
+		auto &structTy = getType(structPtrTy.element);
+		auto &arrayTy = getType(structTy.definition.word(2 + arrayFieldIdx));
+		ASSERT(arrayTy.definition.opcode() == spv::OpTypeRuntimeArray);
+		auto &arrayElTy = getType(arrayTy.element);
+
+		auto &result = state->routine->createIntermediate(resultId, 1);
+		auto structBase = GetPointerToData(structPtrId, 0, state->routine);
+
+		Decorations d = {};
+		ApplyDecorationsForIdMember(&d, structPtrTy.element, arrayFieldIdx);
+		ASSERT(d.HasOffset);
+
+		auto arrayBase = structBase + d.Offset;
+		auto arraySizeInBytes = SIMD::Int(arrayBase.limit) - arrayBase.offsets();
+		auto arrayLength = arraySizeInBytes / SIMD::Int(arrayElTy.sizeInComponents * sizeof(float));
+
+		result.move(0, SIMD::Int(arrayLength));
+
+		return EmitResult::Continue;
+	}
+
 	uint32_t SpirvShader::GetConstScalarInt(Object::ID id) const
 	{
 		auto &scopeObj = getObject(id);
diff --git a/src/Pipeline/SpirvShader.hpp b/src/Pipeline/SpirvShader.hpp
index 292f4d2..3f71fb5 100644
--- a/src/Pipeline/SpirvShader.hpp
+++ b/src/Pipeline/SpirvShader.hpp
@@ -944,6 +944,7 @@
 		EmitResult EmitControlBarrier(InsnIterator insn, EmitState *state) const;
 		EmitResult EmitMemoryBarrier(InsnIterator insn, EmitState *state) const;
 		EmitResult EmitGroupNonUniform(InsnIterator insn, EmitState *state) const;
+		EmitResult EmitArrayLength(InsnIterator insn, EmitState *state) const;
 
 		void GetImageDimensions(SpirvRoutine const *routine, Type const &resultTy, Object::ID imageId, Object::ID lodId, Intermediate &dst) const;
 		SIMD::Pointer GetTexelAddress(SpirvRoutine const *routine, SIMD::Pointer base, GenericValue const & coordinate, Type const & imageType, Pointer<Byte> descriptor, int texelSize, Object::ID sampleId, bool useStencilAspect) const;