Fix integer math overflows in the preprocessor
Evaluating integer expressions in the ESSL preprocessor may result in
overflowing the signed integer range. Implement wrapping overflow for
preprocessor expressions in a way that doesn't hit any undefined
behavior. In the ESSL spec, preprocessor expressions are defined to
have mostly the same semantics as in C++. Since C++ doesn't define
what happens on signed integer overflow, we choose to make most of the
operators wrap on overflow for backward compatibility and consistency
with the rest of the ESSL spec.
We reuse the existing wrapping overflow helpers that are
used for constant folding. To be able to do this, the type used in the
preprocessor expression parser is changed from 64-bit to 32-bit.
Shifting negative numbers is implemented as a logical shift. This
cannot be disallowed since dEQP requires shaders shifting negative
numbers to pass compilation.
Undefined bitwise shifts where the offset is greater than 31 will now
result in a compile-time error.
A couple of test cases are now covered by the preprocessor tests
rather than full compilation tests. This isolates the tests better and
they run faster.
BUG=chromium:652223
TEST=angle_unittests
Change-Id: I84be40d404c10ecd0846c5d477e626a94a2a8587
Reviewed-on: https://chromium-review.googlesource.com/392146
Commit-Queue: Olli Etuaho <oetuaho@nvidia.com>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
diff --git a/src/compiler/translator/ConstantUnion.cpp b/src/compiler/translator/ConstantUnion.cpp
index 90a0042..10a70f3 100644
--- a/src/compiler/translator/ConstantUnion.cpp
+++ b/src/compiler/translator/ConstantUnion.cpp
@@ -8,6 +8,7 @@
#include "compiler/translator/ConstantUnion.h"
#include "base/numerics/safe_math.h"
+#include "common/mathutil.h"
#include "compiler/translator/Diagnostics.h"
namespace
@@ -61,38 +62,6 @@
return result.ValueOrDefault(0);
}
-// Unsigned types are defined to do arithmetic modulo 2^n in C++. For signed types, overflow
-// behavior is undefined.
-
-template <typename T>
-T WrappingSum(T lhs, T rhs)
-{
- uint32_t lhsUnsigned = static_cast<uint32_t>(lhs);
- uint32_t rhsUnsigned = static_cast<uint32_t>(rhs);
- return static_cast<T>(lhsUnsigned + rhsUnsigned);
-}
-
-template <typename T>
-T WrappingDiff(T lhs, T rhs)
-{
- uint32_t lhsUnsigned = static_cast<uint32_t>(lhs);
- uint32_t rhsUnsigned = static_cast<uint32_t>(rhs);
- return static_cast<T>(lhsUnsigned - rhsUnsigned);
-}
-
-int32_t WrappingMul(int32_t lhs, int32_t rhs)
-{
- int64_t lhsWide = static_cast<int64_t>(lhs);
- int64_t rhsWide = static_cast<int64_t>(rhs);
- // The multiplication is guaranteed not to overflow.
- int64_t resultWide = lhsWide * rhsWide;
- // Implement the desired wrapping behavior by masking out the high-order 32 bits.
- resultWide = resultWide & 0xffffffffll;
- // Casting to a narrower signed type is fine since the casted value is representable in the
- // narrower type.
- return static_cast<int32_t>(resultWide);
-}
-
} // anonymous namespace
TConstantUnion::TConstantUnion()
@@ -315,10 +284,10 @@
switch (lhs.type)
{
case EbtInt:
- returnValue.setIConst(WrappingSum<int>(lhs.iConst, rhs.iConst));
+ returnValue.setIConst(gl::WrappingSum<int>(lhs.iConst, rhs.iConst));
break;
case EbtUInt:
- returnValue.setUConst(WrappingSum<unsigned int>(lhs.uConst, rhs.uConst));
+ returnValue.setUConst(gl::WrappingSum<unsigned int>(lhs.uConst, rhs.uConst));
break;
case EbtFloat:
returnValue.setFConst(CheckedSum<float>(lhs.fConst, rhs.fConst, diag, line));
@@ -341,10 +310,10 @@
switch (lhs.type)
{
case EbtInt:
- returnValue.setIConst(WrappingDiff<int>(lhs.iConst, rhs.iConst));
+ returnValue.setIConst(gl::WrappingDiff<int>(lhs.iConst, rhs.iConst));
break;
case EbtUInt:
- returnValue.setUConst(WrappingDiff<unsigned int>(lhs.uConst, rhs.uConst));
+ returnValue.setUConst(gl::WrappingDiff<unsigned int>(lhs.uConst, rhs.uConst));
break;
case EbtFloat:
returnValue.setFConst(CheckedDiff<float>(lhs.fConst, rhs.fConst, diag, line));
@@ -367,7 +336,7 @@
switch (lhs.type)
{
case EbtInt:
- returnValue.setIConst(WrappingMul(lhs.iConst, rhs.iConst));
+ returnValue.setIConst(gl::WrappingMul(lhs.iConst, rhs.iConst));
break;
case EbtUInt:
// Unsigned integer math in C++ is defined to be done in modulo 2^n, so we rely on that