SpirvShader: Implement SMod.

Test: *smod*
Bug: b/126873455
Change-Id: If946e72d23aa9386b85cffe96b02ab63195f0a24
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/26549
Tested-by: Ben Clayton <bclayton@google.com>
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
diff --git a/src/Pipeline/SpirvShader.cpp b/src/Pipeline/SpirvShader.cpp
index 228c01c..25ce989 100644
--- a/src/Pipeline/SpirvShader.cpp
+++ b/src/Pipeline/SpirvShader.cpp
@@ -302,6 +302,7 @@
 			case spv::OpFUnordLessThanEqual:
 			case spv::OpFOrdGreaterThanEqual:
 			case spv::OpFUnordGreaterThanEqual:
+			case spv::OpSMod:
 			case spv::OpUMod:
 			case spv::OpIEqual:
 			case spv::OpINotEqual:
@@ -943,7 +944,7 @@
 				// TODO: what to do about zero-slot objects?
 				if (pointeeTy.sizeInComponents > 0)
 				{
-					routine->createLvalue(insn.word(2), pointeeTy.sizeInComponents);
+					routine->createLvalue(resultId, pointeeTy.sizeInComponents);
 				}
 				break;
 			}
@@ -1081,6 +1082,7 @@
 			case spv::OpFUnordLessThanEqual:
 			case spv::OpFOrdGreaterThanEqual:
 			case spv::OpFUnordGreaterThanEqual:
+			case spv::OpSMod:
 			case spv::OpUMod:
 			case spv::OpIEqual:
 			case spv::OpINotEqual:
@@ -1541,6 +1543,22 @@
 			case spv::OpUDiv:
 				dst.emplace(i, As<SIMD::Float>(As<SIMD::UInt>(lhs) / As<SIMD::UInt>(rhs)));
 				break;
+			case spv::OpSMod:
+			{
+				auto a = As<SIMD::Int>(lhs);
+				auto b = As<SIMD::Int>(rhs);
+				auto mod = a % b;
+				// If a and b have opposite signs, the remainder operation takes
+				// the sign from a but OpSMod is supposed to take the sign of b.
+				// Adding b will ensure that the result has the correct sign and
+				// that it is still congruent to a modulo b.
+				//
+				// See also http://mathforum.org/library/drmath/view/52343.html
+				auto signDiff = CmpNEQ(CmpGE(a, SIMD::Int(0)), CmpGE(b, SIMD::Int(0)));
+				auto fixedMod = mod + (b & CmpNEQ(mod, SIMD::Int(0)) & signDiff);
+				dst.emplace(i, As<SIMD::Float>(fixedMod));
+				break;
+			}
 			case spv::OpUMod:
 				dst.emplace(i, As<SIMD::Float>(As<SIMD::UInt>(lhs) % As<SIMD::UInt>(rhs)));
 				break;