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;