SpirvShader: Implement GLSLstd450NMax

Beware: the dEQP tests here are very weak - they do not seem to properly test the NaN handling of this instruction.

Bug: b/126873455
Tests: dEQP-VK.spirv_assembly.instruction.compute.opnmax.all
Change-Id: Ibd704c9dc9c3475df10b02c0c0f80b9c3e472ff4
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/28710
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
Tested-by: Ben Clayton <bclayton@google.com>
Kokoro-Presubmit: kokoro <noreply+kokoro@google.com>
diff --git a/src/Pipeline/SpirvShader.cpp b/src/Pipeline/SpirvShader.cpp
index 8ac76c2..412a0af 100644
--- a/src/Pipeline/SpirvShader.cpp
+++ b/src/Pipeline/SpirvShader.cpp
@@ -86,6 +86,23 @@
 			((~xIsNan &  yIsNan) & As<sw::SIMD::Int>(x)) |
 			(( xIsNan          ) & As<sw::SIMD::Int>(y)));
 	}
+
+	// Returns y if y > x; otherwise result is x.
+	// If one operand is a NaN, the other operand is the result.
+	// If both operands are NaN, the result is a NaN.
+	rr::RValue<sw::SIMD::Float> NMax(rr::RValue<sw::SIMD::Float> const &x, rr::RValue<sw::SIMD::Float> const &y)
+	{
+		using namespace rr;
+		auto xIsNan = IsNan(x);
+		auto yIsNan = IsNan(y);
+		return As<sw::SIMD::Float>(
+			// If neither are NaN, return max
+			((~xIsNan & ~yIsNan) & As<sw::SIMD::Int>(Max(x, y))) |
+			// If one operand is a NaN, the other operand is the result
+			// If both operands are NaN, the result is a NaN.
+			((~xIsNan &  yIsNan) & As<sw::SIMD::Int>(x)) |
+			(( xIsNan          ) & As<sw::SIMD::Int>(y)));
+	}
 }
 
 namespace sw
@@ -3566,7 +3583,12 @@
 		}
 		case GLSLstd450NMax:
 		{
-			UNIMPLEMENTED("GLSLstd450NMax");
+			auto x = GenericValue(this, routine, insn.word(5));
+			auto y = GenericValue(this, routine, insn.word(6));
+			for (auto i = 0u; i < type.sizeInComponents; i++)
+			{
+				dst.move(i, NMax(x.Float(i), y.Float(i)));
+			}
 			break;
 		}
 		case GLSLstd450NClamp: