OpSelect implementation for pointers

Buffer Device Address adds the ability to perform an OpSelect
operation on pointers. This should only affect physical storage
pointers.

Tested locally using:
dEQP-VK.spirv_assembly.instruction.compute.physical_pointers.compute.reads_opselect_*
(These tests require the Buffer Device Address feature).

Bug: b/184952772
Change-Id: I75b999470e150ecb6bf9979ed900c21ffb2713b8
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/66288
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
Commit-Queue: Alexis Hétu <sugoi@google.com>
Tested-by: Alexis Hétu <sugoi@google.com>
Kokoro-Result: kokoro <noreply+kokoro@google.com>
diff --git a/src/Pipeline/SpirvShader.cpp b/src/Pipeline/SpirvShader.cpp
index 1a7dff9..c6377ed 100644
--- a/src/Pipeline/SpirvShader.cpp
+++ b/src/Pipeline/SpirvShader.cpp
@@ -2384,19 +2384,37 @@
 SpirvShader::EmitResult SpirvShader::EmitSelect(InsnIterator insn, EmitState *state) const
 {
 	auto &type = getType(insn.resultTypeId());
-	auto &dst = state->createIntermediate(insn.resultId(), type.componentCount);
+	auto result = getObject(insn.resultId());
 	auto cond = Operand(this, state, insn.word(3));
 	auto condIsScalar = (cond.componentCount == 1);
-	auto lhs = Operand(this, state, insn.word(4));
-	auto rhs = Operand(this, state, insn.word(5));
 
-	for(auto i = 0u; i < type.componentCount; i++)
+	switch(result.kind)
 	{
-		auto sel = cond.Int(condIsScalar ? 0 : i);
-		dst.move(i, (sel & lhs.Int(i)) | (~sel & rhs.Int(i)));  // TODO: IfThenElse()
+	case Object::Kind::Pointer:
+		{
+			ASSERT(condIsScalar);
+			ASSERT(type.storageClass == spv::StorageClassPhysicalStorageBuffer);
+
+			auto &lhs = state->getPointer(insn.word(4));
+			auto &rhs = state->getPointer(insn.word(5));
+			state->createPointer(insn.resultId(), SIMD::Pointer::IfThenElse(cond.Int(0), lhs, rhs));
+		}
+		break;
+	default:
+		{
+			auto lhs = Operand(this, state, insn.word(4));
+			auto rhs = Operand(this, state, insn.word(5));
+			auto &dst = state->createIntermediate(insn.resultId(), type.componentCount);
+			for(auto i = 0u; i < type.componentCount; i++)
+			{
+				auto sel = cond.Int(condIsScalar ? 0 : i);
+				dst.move(i, (sel & lhs.Int(i)) | (~sel & rhs.Int(i)));  // TODO: IfThenElse()
+			}
+		}
+		break;
 	}
 
-	SPIRV_SHADER_DBG("{0}: {1}", insn.word(2), dst);
+	SPIRV_SHADER_DBG("{0}: {1}", insn.word(2), result);
 	SPIRV_SHADER_DBG("{0}: {1}", insn.word(3), cond);
 	SPIRV_SHADER_DBG("{0}: {1}", insn.word(4), lhs);
 	SPIRV_SHADER_DBG("{0}: {1}", insn.word(5), rhs);
diff --git a/src/Reactor/Reactor.cpp b/src/Reactor/Reactor.cpp
index 83b8c38..f6f12ca 100644
--- a/src/Reactor/Reactor.cpp
+++ b/src/Reactor/Reactor.cpp
@@ -4621,6 +4621,23 @@
 	}
 }
 
+Pointer4 Pointer4::IfThenElse(Int4 condition, const Pointer4 &lhs, const Pointer4 &rhs)
+{
+	std::array<Pointer<Byte>, 4> pointers;
+	for(int i = 0; i < 4; i++)
+	{
+		If(Extract(condition, i) != 0)
+		{
+			pointers[i] = lhs.getPointerForLane(i);
+		}
+		Else
+		{
+			pointers[i] = rhs.getPointerForLane(i);
+		}
+	}
+	return { pointers };
+}
+
 #ifdef ENABLE_RR_PRINT
 std::vector<rr::Value *> Pointer4::getPrintValues() const
 {
diff --git a/src/Reactor/Reactor.hpp b/src/Reactor/Reactor.hpp
index 175e2d8..1c1ca43 100644
--- a/src/Reactor/Reactor.hpp
+++ b/src/Reactor/Reactor.hpp
@@ -2203,6 +2203,7 @@
 
 	Pointer<Byte> getUniformPointer() const;
 	Pointer<Byte> getPointerForLane(int lane) const;
+	static Pointer4 IfThenElse(Int4 condition, const Pointer4 &lhs, const Pointer4 &rhs);
 
 #ifdef ENABLE_RR_PRINT
 	std::vector<rr::Value *> getPrintValues() const;
@@ -3076,7 +3077,7 @@
 
 // Calls the static function pointer fptr with the given arguments args.
 template<typename Return, typename... CArgs, typename... RArgs>
-inline CToReactorT<Return> Call(Return(fptr)(CArgs...), RArgs &&...args)
+inline CToReactorT<Return> Call(Return(fptr)(CArgs...), RArgs &&... args)
 {
 	return CallHelper<Return(CArgs...)>::Call(fptr, CastToReactor(std::forward<RArgs>(args))...);
 }
@@ -3084,7 +3085,7 @@
 // Calls the static function pointer fptr with the given arguments args.
 // Overload for calling functions with void return type.
 template<typename... CArgs, typename... RArgs>
-inline void Call(void(fptr)(CArgs...), RArgs &&...args)
+inline void Call(void(fptr)(CArgs...), RArgs &&... args)
 {
 	CallHelper<void(CArgs...)>::Call(fptr, CastToReactor(std::forward<RArgs>(args))...);
 }
@@ -3092,7 +3093,7 @@
 // Calls the member function pointer fptr with the given arguments args.
 // object can be a Class*, or a Pointer<Byte>.
 template<typename Return, typename Class, typename C, typename... CArgs, typename... RArgs>
-inline CToReactorT<Return> Call(Return (Class::*fptr)(CArgs...), C &&object, RArgs &&...args)
+inline CToReactorT<Return> Call(Return (Class::*fptr)(CArgs...), C &&object, RArgs &&... args)
 {
 	using Helper = CallHelper<Return(Class *, void *, CArgs...)>;
 	using fptrTy = decltype(fptr);
@@ -3116,7 +3117,7 @@
 // Overload for calling functions with void return type.
 // object can be a Class*, or a Pointer<Byte>.
 template<typename Class, typename C, typename... CArgs, typename... RArgs>
-inline void Call(void (Class::*fptr)(CArgs...), C &&object, RArgs &&...args)
+inline void Call(void (Class::*fptr)(CArgs...), C &&object, RArgs &&... args)
 {
 	using Helper = CallHelper<void(Class *, void *, CArgs...)>;
 	using fptrTy = decltype(fptr);
@@ -3173,7 +3174,7 @@
 // Calls the Reactor function pointer fptr with the signature FUNCTION_SIGNATURE and arguments.
 // Overload for calling functions with non-void return type.
 template<typename FUNCTION_SIGNATURE, typename... RArgs>
-inline CToReactorT<NonVoidFunctionReturnType<FUNCTION_SIGNATURE>> Call(Pointer<Byte> fptr, RArgs &&...args)
+inline CToReactorT<NonVoidFunctionReturnType<FUNCTION_SIGNATURE>> Call(Pointer<Byte> fptr, RArgs &&... args)
 {
 	return CallHelper<FUNCTION_SIGNATURE>::Call(fptr, CastToReactor(std::forward<RArgs>(args))...);
 }
@@ -3181,7 +3182,7 @@
 // Calls the Reactor function pointer fptr with the signature FUNCTION_SIGNATURE and arguments.
 // Overload for calling functions with void return type.
 template<typename FUNCTION_SIGNATURE, typename... RArgs>
-inline VoidFunctionReturnType<FUNCTION_SIGNATURE> Call(Pointer<Byte> fptr, RArgs &&...args)
+inline VoidFunctionReturnType<FUNCTION_SIGNATURE> Call(Pointer<Byte> fptr, RArgs &&... args)
 {
 	CallHelper<FUNCTION_SIGNATURE>::Call(fptr, CastToReactor(std::forward<RArgs>(args))...);
 }