// // Copyright 2016 The ANGLE Project Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // // ConstantUnion: Constant folding helper class. #include "compiler/translator/ConstantUnion.h" #include "base/numerics/safe_math.h" #include "common/mathutil.h" #include "compiler/translator/Diagnostics.h" namespace sh { namespace { template T CheckedSum(base::CheckedNumeric lhs, base::CheckedNumeric rhs, TDiagnostics *diag, const TSourceLoc &line) { ASSERT(lhs.IsValid() && rhs.IsValid()); auto result = lhs + rhs; if (!result.IsValid()) { diag->error(line, "Addition out of range", "*", ""); return 0; } return result.ValueOrDefault(0); } template T CheckedDiff(base::CheckedNumeric lhs, base::CheckedNumeric rhs, TDiagnostics *diag, const TSourceLoc &line) { ASSERT(lhs.IsValid() && rhs.IsValid()); auto result = lhs - rhs; if (!result.IsValid()) { diag->error(line, "Difference out of range", "*", ""); return 0; } return result.ValueOrDefault(0); } template T CheckedMul(base::CheckedNumeric lhs, base::CheckedNumeric rhs, TDiagnostics *diag, const TSourceLoc &line) { ASSERT(lhs.IsValid() && rhs.IsValid()); auto result = lhs * rhs; if (!result.IsValid()) { diag->error(line, "Multiplication out of range", "*", ""); return 0; } return result.ValueOrDefault(0); } } // anonymous namespace TConstantUnion::TConstantUnion() { iConst = 0; type = EbtVoid; } bool TConstantUnion::cast(TBasicType newType, const TConstantUnion &constant) { switch (newType) { case EbtFloat: switch (constant.type) { case EbtInt: setFConst(static_cast(constant.getIConst())); break; case EbtUInt: setFConst(static_cast(constant.getUConst())); break; case EbtBool: setFConst(static_cast(constant.getBConst())); break; case EbtFloat: setFConst(static_cast(constant.getFConst())); break; default: return false; } break; case EbtInt: switch (constant.type) { case EbtInt: setIConst(static_cast(constant.getIConst())); break; case EbtUInt: setIConst(static_cast(constant.getUConst())); break; case EbtBool: setIConst(static_cast(constant.getBConst())); break; case EbtFloat: setIConst(static_cast(constant.getFConst())); break; default: return false; } break; case EbtUInt: switch (constant.type) { case EbtInt: setUConst(static_cast(constant.getIConst())); break; case EbtUInt: setUConst(static_cast(constant.getUConst())); break; case EbtBool: setUConst(static_cast(constant.getBConst())); break; case EbtFloat: setUConst(static_cast(constant.getFConst())); break; default: return false; } break; case EbtBool: switch (constant.type) { case EbtInt: setBConst(constant.getIConst() != 0); break; case EbtUInt: setBConst(constant.getUConst() != 0); break; case EbtBool: setBConst(constant.getBConst()); break; case EbtFloat: setBConst(constant.getFConst() != 0.0f); break; default: return false; } break; case EbtStruct: // Struct fields don't get cast switch (constant.type) { case EbtInt: setIConst(constant.getIConst()); break; case EbtUInt: setUConst(constant.getUConst()); break; case EbtBool: setBConst(constant.getBConst()); break; case EbtFloat: setFConst(constant.getFConst()); break; default: return false; } break; default: return false; } return true; } bool TConstantUnion::operator==(const int i) const { return i == iConst; } bool TConstantUnion::operator==(const unsigned int u) const { return u == uConst; } bool TConstantUnion::operator==(const float f) const { return f == fConst; } bool TConstantUnion::operator==(const bool b) const { return b == bConst; } bool TConstantUnion::operator==(const TConstantUnion &constant) const { if (constant.type != type) return false; switch (type) { case EbtInt: return constant.iConst == iConst; case EbtUInt: return constant.uConst == uConst; case EbtFloat: return constant.fConst == fConst; case EbtBool: return constant.bConst == bConst; default: return false; } } bool TConstantUnion::operator!=(const int i) const { return !operator==(i); } bool TConstantUnion::operator!=(const unsigned int u) const { return !operator==(u); } bool TConstantUnion::operator!=(const float f) const { return !operator==(f); } bool TConstantUnion::operator!=(const bool b) const { return !operator==(b); } bool TConstantUnion::operator!=(const TConstantUnion &constant) const { return !operator==(constant); } bool TConstantUnion::operator>(const TConstantUnion &constant) const { ASSERT(type == constant.type); switch (type) { case EbtInt: return iConst > constant.iConst; case EbtUInt: return uConst > constant.uConst; case EbtFloat: return fConst > constant.fConst; default: return false; // Invalid operation, handled at semantic analysis } } bool TConstantUnion::operator<(const TConstantUnion &constant) const { ASSERT(type == constant.type); switch (type) { case EbtInt: return iConst < constant.iConst; case EbtUInt: return uConst < constant.uConst; case EbtFloat: return fConst < constant.fConst; default: return false; // Invalid operation, handled at semantic analysis } } // static TConstantUnion TConstantUnion::add(const TConstantUnion &lhs, const TConstantUnion &rhs, TDiagnostics *diag, const TSourceLoc &line) { TConstantUnion returnValue; ASSERT(lhs.type == rhs.type); switch (lhs.type) { case EbtInt: returnValue.setIConst(gl::WrappingSum(lhs.iConst, rhs.iConst)); break; case EbtUInt: returnValue.setUConst(gl::WrappingSum(lhs.uConst, rhs.uConst)); break; case EbtFloat: returnValue.setFConst(CheckedSum(lhs.fConst, rhs.fConst, diag, line)); break; default: UNREACHABLE(); } return returnValue; } // static TConstantUnion TConstantUnion::sub(const TConstantUnion &lhs, const TConstantUnion &rhs, TDiagnostics *diag, const TSourceLoc &line) { TConstantUnion returnValue; ASSERT(lhs.type == rhs.type); switch (lhs.type) { case EbtInt: returnValue.setIConst(gl::WrappingDiff(lhs.iConst, rhs.iConst)); break; case EbtUInt: returnValue.setUConst(gl::WrappingDiff(lhs.uConst, rhs.uConst)); break; case EbtFloat: returnValue.setFConst(CheckedDiff(lhs.fConst, rhs.fConst, diag, line)); break; default: UNREACHABLE(); } return returnValue; } // static TConstantUnion TConstantUnion::mul(const TConstantUnion &lhs, const TConstantUnion &rhs, TDiagnostics *diag, const TSourceLoc &line) { TConstantUnion returnValue; ASSERT(lhs.type == rhs.type); switch (lhs.type) { case EbtInt: 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 // to implement wrapping multiplication. returnValue.setUConst(lhs.uConst * rhs.uConst); break; case EbtFloat: returnValue.setFConst(CheckedMul(lhs.fConst, rhs.fConst, diag, line)); break; default: UNREACHABLE(); } return returnValue; } TConstantUnion TConstantUnion::operator%(const TConstantUnion &constant) const { TConstantUnion returnValue; ASSERT(type == constant.type); switch (type) { case EbtInt: returnValue.setIConst(iConst % constant.iConst); break; case EbtUInt: returnValue.setUConst(uConst % constant.uConst); break; default: UNREACHABLE(); } return returnValue; } // static TConstantUnion TConstantUnion::rshift(const TConstantUnion &lhs, const TConstantUnion &rhs, TDiagnostics *diag, const TSourceLoc &line) { TConstantUnion returnValue; ASSERT(lhs.type == EbtInt || lhs.type == EbtUInt); ASSERT(rhs.type == EbtInt || rhs.type == EbtUInt); if ((rhs.type == EbtInt && (rhs.iConst < 0 || rhs.iConst > 31)) || (rhs.type == EbtUInt && rhs.uConst > 31u)) { diag->error(line, "Undefined shift (operand out of range)", ">>", ""); switch (lhs.type) { case EbtInt: returnValue.setIConst(0); break; case EbtUInt: returnValue.setUConst(0u); break; default: UNREACHABLE(); } return returnValue; } switch (lhs.type) { case EbtInt: { unsigned int shiftOffset = 0; switch (rhs.type) { case EbtInt: shiftOffset = static_cast(rhs.iConst); break; case EbtUInt: shiftOffset = rhs.uConst; break; default: UNREACHABLE(); } if (shiftOffset > 0) { // ESSL 3.00.6 section 5.9: "If E1 is a signed integer, the right-shift will extend // the sign bit." In C++ shifting negative integers is undefined, so we implement // extending the sign bit manually. int lhsSafe = lhs.iConst; if (lhsSafe == std::numeric_limits::min()) { // The min integer needs special treatment because only bit it has set is the // sign bit, which we clear later to implement safe right shift of negative // numbers. lhsSafe = -0x40000000; --shiftOffset; } if (shiftOffset > 0) { bool extendSignBit = false; if (lhsSafe < 0) { extendSignBit = true; // Clear the sign bit so that bitshift right is defined in C++. lhsSafe &= 0x7fffffff; ASSERT(lhsSafe > 0); } returnValue.setIConst(lhsSafe >> shiftOffset); // Manually fill in the extended sign bit if necessary. if (extendSignBit) { int extendedSignBit = static_cast(0xffffffffu << (31 - shiftOffset)); returnValue.setIConst(returnValue.getIConst() | extendedSignBit); } } else { returnValue.setIConst(lhsSafe); } } else { returnValue.setIConst(lhs.iConst); } break; } case EbtUInt: switch (rhs.type) { case EbtInt: returnValue.setUConst(lhs.uConst >> rhs.iConst); break; case EbtUInt: returnValue.setUConst(lhs.uConst >> rhs.uConst); break; default: UNREACHABLE(); } break; default: UNREACHABLE(); } return returnValue; } // static TConstantUnion TConstantUnion::lshift(const TConstantUnion &lhs, const TConstantUnion &rhs, TDiagnostics *diag, const TSourceLoc &line) { TConstantUnion returnValue; ASSERT(lhs.type == EbtInt || lhs.type == EbtUInt); ASSERT(rhs.type == EbtInt || rhs.type == EbtUInt); if ((rhs.type == EbtInt && (rhs.iConst < 0 || rhs.iConst > 31)) || (rhs.type == EbtUInt && rhs.uConst > 31u)) { diag->error(line, "Undefined shift (operand out of range)", "<<", ""); switch (lhs.type) { case EbtInt: returnValue.setIConst(0); break; case EbtUInt: returnValue.setUConst(0u); break; default: UNREACHABLE(); } return returnValue; } switch (lhs.type) { case EbtInt: switch (rhs.type) { // Cast to unsigned integer before shifting, since ESSL 3.00.6 section 5.9 says that // lhs is "interpreted as a bit pattern". This also avoids the possibility of signed // integer overflow or undefined shift of a negative integer. case EbtInt: returnValue.setIConst( static_cast(static_cast(lhs.iConst) << rhs.iConst)); break; case EbtUInt: returnValue.setIConst( static_cast(static_cast(lhs.iConst) << rhs.uConst)); break; default: UNREACHABLE(); } break; case EbtUInt: switch (rhs.type) { case EbtInt: returnValue.setUConst(lhs.uConst << rhs.iConst); break; case EbtUInt: returnValue.setUConst(lhs.uConst << rhs.uConst); break; default: UNREACHABLE(); } break; default: UNREACHABLE(); } return returnValue; } TConstantUnion TConstantUnion::operator&(const TConstantUnion &constant) const { TConstantUnion returnValue; ASSERT(constant.type == EbtInt || constant.type == EbtUInt); switch (type) { case EbtInt: returnValue.setIConst(iConst & constant.iConst); break; case EbtUInt: returnValue.setUConst(uConst & constant.uConst); break; default: UNREACHABLE(); } return returnValue; } TConstantUnion TConstantUnion::operator|(const TConstantUnion &constant) const { TConstantUnion returnValue; ASSERT(type == constant.type); switch (type) { case EbtInt: returnValue.setIConst(iConst | constant.iConst); break; case EbtUInt: returnValue.setUConst(uConst | constant.uConst); break; default: UNREACHABLE(); } return returnValue; } TConstantUnion TConstantUnion::operator^(const TConstantUnion &constant) const { TConstantUnion returnValue; ASSERT(type == constant.type); switch (type) { case EbtInt: returnValue.setIConst(iConst ^ constant.iConst); break; case EbtUInt: returnValue.setUConst(uConst ^ constant.uConst); break; default: UNREACHABLE(); } return returnValue; } TConstantUnion TConstantUnion::operator&&(const TConstantUnion &constant) const { TConstantUnion returnValue; ASSERT(type == constant.type); switch (type) { case EbtBool: returnValue.setBConst(bConst && constant.bConst); break; default: UNREACHABLE(); } return returnValue; } TConstantUnion TConstantUnion::operator||(const TConstantUnion &constant) const { TConstantUnion returnValue; ASSERT(type == constant.type); switch (type) { case EbtBool: returnValue.setBConst(bConst || constant.bConst); break; default: UNREACHABLE(); } return returnValue; } } // namespace sh