SpirvShader: Precision fixes for GLSLstd450Ldexp

Bug: b/126873455
Tests: dEQP-VK.glsl.builtin.precision.ldexp.*
Tests: dEQP-VK.glsl.builtin.function.common.ldexp.*
Change-Id: If70125d749ba976abfa7295f067be51c5e8c2417
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/31409
Presubmit-Ready: Ben Clayton <bclayton@google.com>
Kokoro-Presubmit: kokoro <noreply+kokoro@google.com>
Tested-by: Ben Clayton <bclayton@google.com>
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
Reviewed-by: Chris Forbes <chrisforbes@google.com>
diff --git a/src/Pipeline/SpirvShader.cpp b/src/Pipeline/SpirvShader.cpp
index 9a507bf..1522c91 100644
--- a/src/Pipeline/SpirvShader.cpp
+++ b/src/Pipeline/SpirvShader.cpp
@@ -3955,10 +3955,29 @@
 			for (auto i = 0u; i < type.sizeInComponents; i++)
 			{
 				// Assumes IEEE 754
-				auto significandExponent = Exponent(significand.Float(i));
+				auto in = significand.Float(i);
+				auto significandExponent = Exponent(in);
 				auto combinedExponent = exponent.Int(i) + significandExponent;
-				SIMD::UInt v = (significand.UInt(i) & SIMD::UInt(0x807FFFFF)) |
-						(SIMD::UInt(combinedExponent + SIMD::Int(126)) << SIMD::UInt(23));
+				auto isSignificandZero     = SIMD::UInt(CmpEQ(significand.Int(0), SIMD::Int(0)));
+				auto isSignificandInf      = SIMD::UInt(IsInf(in));
+				auto isSignificandNaN      = SIMD::UInt(IsNan(in));
+				auto isExponentNotTooSmall = SIMD::UInt(CmpGE(combinedExponent, SIMD::Int(-126)));
+				auto isExponentNotTooLarge = SIMD::UInt(CmpLE(combinedExponent, SIMD::Int(128)));
+				auto isExponentInBounds    = isExponentNotTooSmall & isExponentNotTooLarge;
+
+				SIMD::UInt v;
+				v  = significand.UInt(i) & SIMD::UInt(0x7FFFFF); // Add significand.
+				v |= (SIMD::UInt(combinedExponent + SIMD::Int(126)) << SIMD::UInt(23)); // Add exponent.
+				v &= isExponentInBounds; // Clear v if the exponent is OOB.
+
+				v |= significand.UInt(i) & SIMD::UInt(0x80000000); // Add sign bit.
+				v |= ~isExponentNotTooLarge & SIMD::UInt(0x7F800000); // Mark as inf if the exponent is too great.
+
+				// If the input significand is zero, inf or nan, just return the
+				// input significand.
+				auto passthrough = isSignificandZero | isSignificandInf | isSignificandNaN;
+				v = (v & ~passthrough) | (significand.UInt(0) & passthrough);
+
 				dst.move(i, As<SIMD::Float>(v));
 			}
 			break;