SpirvShader: Implement GLSLstd450Frexp

Bug: b/126873455
Tests: dEQP-VK.glsl.builtin.function.common.frexp.*
Change-Id: Ic725c53191c3a6ec6494859d6eb8e6a8eb77c90e
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/28628
Tested-by: Ben Clayton <bclayton@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 8a8077a..0c1f43d 100644
--- a/src/Pipeline/SpirvShader.cpp
+++ b/src/Pipeline/SpirvShader.cpp
@@ -3179,6 +3179,48 @@
 			dst.move(1, HalfToFloatBits((val.UInt(0) & SIMD::UInt(0xFFFF0000)) >> 16));
 			break;
 		}
+		case GLSLstd450Frexp:
+		{
+			auto val = GenericValue(this, routine, insn.word(5));
+			auto ptrId = Object::ID(insn.word(6));
+			auto ptrTy = getType(getObject(ptrId).type);
+			auto ptr = GetPointerToData(ptrId, 0, routine);
+			bool interleavedByLane = IsStorageInterleavedByLane(ptrTy.storageClass);
+
+			for (auto i = 0u; i < type.sizeInComponents; i++)
+			{
+				SIMD::Float significand;
+				SIMD::Int exponent;
+				std::tie(significand, exponent) = Frexp(val.Float(i));
+
+				dst.move(i, significand);
+
+				// TODO: Refactor and consolidate with EmitStore.
+				for (int j = 0; j < SIMD::Width; j++)
+				{
+					auto ptrBase = Pointer<Int>(ptr.base);
+					If(Extract(state->activeLaneMask(), j) != 0)
+					{
+						Int offset = Int(i) + Extract(ptr.offset, j);
+						if (interleavedByLane) { offset = offset * SIMD::Width + j; }
+						Store(Extract(exponent, j), &ptrBase[offset], sizeof(uint32_t), false, std::memory_order_relaxed);
+					}
+				}
+			}
+			break;
+		}
+		case GLSLstd450FrexpStruct:
+		{
+			auto val = GenericValue(this, routine, insn.word(5));
+			auto numComponents = getType(val.type).sizeInComponents;
+			for (auto i = 0u; i < numComponents; i++)
+			{
+				auto significandAndExponent = Frexp(val.Float(i));
+				dst.move(i, significandAndExponent.first);
+				dst.move(i + numComponents, significandAndExponent.second);
+			}
+			break;
+		}
 		default:
 			UNIMPLEMENTED("Unhandled ExtInst %d", extInstIndex);
 		}
@@ -3250,6 +3292,17 @@
 						 (CmpNLE(As<SIMD::UInt>(expmant), SIMD::UInt(was_infnan)) & SIMD::UInt(exp_infnan));
 	}
 
+	std::pair<SIMD::Float, SIMD::Int> SpirvShader::Frexp(RValue<SIMD::Float> val) const
+	{
+		// Assumes IEEE 754
+		auto v = As<SIMD::UInt>(val);
+		auto isNotZero = CmpNEQ(v & SIMD::UInt(0x7FFFFFFF), SIMD::UInt(0));
+		auto zeroSign = v & SIMD::UInt(0x80000000) & ~isNotZero;
+		auto significand = As<SIMD::Float>((v & SIMD::UInt(0x807FFFFF) | SIMD::UInt(0x3F000000)) & isNotZero | zeroSign);
+		auto exponent = (SIMD::Int((v >> SIMD::UInt(23)) & SIMD::UInt(0xFF)) - SIMD::Int(126)) & SIMD::Int(isNotZero);
+		return std::make_pair(significand, exponent);
+	}
+
 	SpirvShader::EmitResult SpirvShader::EmitAny(InsnIterator insn, EmitState *state) const
 	{
 		auto routine = state->routine;
diff --git a/src/Pipeline/SpirvShader.hpp b/src/Pipeline/SpirvShader.hpp
index 494ea6b..6254af6 100644
--- a/src/Pipeline/SpirvShader.hpp
+++ b/src/Pipeline/SpirvShader.hpp
@@ -691,8 +691,15 @@
 
 		// Helper as we often need to take dot products as part of doing other things.
 		SIMD::Float Dot(unsigned numComponents, GenericValue const & x, GenericValue const & y) const;
+
 		SIMD::UInt FloatToHalfBits(SIMD::UInt floatBits, bool storeInUpperBits) const;
 		SIMD::UInt HalfToFloatBits(SIMD::UInt halfBits) const;
+
+		// Splits x into a floating-point significand in the range [0.5, 1.0)
+		// and an integral exponent of two, such that:
+		//   x = significand * 2^exponent
+		// Returns the pair <significand, exponent>
+		std::pair<SIMD::Float, SIMD::Int> Frexp(RValue<SIMD::Float> val) const;
 	};
 
 	class SpirvRoutine
diff --git a/src/Reactor/Reactor.cpp b/src/Reactor/Reactor.cpp
index 80d12e0..96a8d25 100644
--- a/src/Reactor/Reactor.cpp
+++ b/src/Reactor/Reactor.cpp
@@ -3572,6 +3572,16 @@
 		return RValue<UInt4>(Nucleus::createNot(val.value));
 	}
 
+	RValue<UInt> Extract(RValue<UInt4> x, int i)
+	{
+		return RValue<UInt>(Nucleus::createExtractElement(x.value, Int::getType(), i));
+	}
+
+	RValue<UInt4> Insert(RValue<UInt4> x, RValue<UInt> element, int i)
+	{
+		return RValue<UInt4>(Nucleus::createInsertElement(x.value, element.value, i));
+	}
+
 	Half::Half(RValue<Float> cast)
 	{
 		UInt fp32i = As<UInt>(cast);
diff --git a/src/Reactor/Reactor.hpp b/src/Reactor/Reactor.hpp
index 93613b2..bb32a46 100644
--- a/src/Reactor/Reactor.hpp
+++ b/src/Reactor/Reactor.hpp
@@ -1972,6 +1972,8 @@
 	RValue<UInt4> Max(RValue<UInt4> x, RValue<UInt4> y);
 	RValue<UInt4> Min(RValue<UInt4> x, RValue<UInt4> y);
 	RValue<UInt4> MulHigh(RValue<UInt4> x, RValue<UInt4> y);
+	RValue<UInt> Extract(RValue<UInt4> val, int i);
+	RValue<UInt4> Insert(RValue<UInt4> val, RValue<UInt> element, int i);
 //	RValue<UInt4> RoundInt(RValue<Float4> cast);
 
 	class Half : public LValue<Half>