Edit

kc3-lang/angle/src/compiler/translator/ConstantUnion.cpp

Branch :

  • Show log

    Commit

  • Author : Shahbaz Youssefi
    Date : 2019-07-17 15:46:29
    Hash : c13ca2af
    Message : Vulkan: Allow more than one atomic counter buffer binding dEQP assumes there are more than one atomic counter buffers available. This is technically not a requirement by the standard, but nevertheless could be what applications expect as well. This change adds support for multiple atomic counter buffer bindings. This is done by declaring an array of storage buffers for the atomic counter buffers (instead of declaring only one) and passing the (binding, offset) pair around to functions instead of just the offset. The atomic counter is found by indexing `binding` into the storage buffer array first before indexing `offset` into its `uint[]`. ProgramVk's default uniform collection is also fixed not to include atomic counter uniforms. A remaining issue is that atomic counter buffer offsets don't have alignment requirements in GLES, but Vulkan does for storage buffers. Similar to emulated transform feedback buffer offsets, these should be sent to the shader through uniform values. This will be done in a follow up change. Bug: angleproject:3566 Change-Id: I5600225c24c38f1a8ecf5c64388073055733197d Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/1707931 Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org> Reviewed-by: Jamie Madill <jmadill@chromium.org>

  • src/compiler/translator/ConstantUnion.cpp
  • //
    // 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 "common/mathutil.h"
    #include "compiler/translator/Diagnostics.h"
    #include "compiler/translator/util.h"
    
    namespace sh
    {
    
    namespace
    {
    
    float CheckedSum(float lhs, float rhs, TDiagnostics *diag, const TSourceLoc &line)
    {
        float result = lhs + rhs;
        if (gl::isNaN(result) && !gl::isNaN(lhs) && !gl::isNaN(rhs))
        {
            diag->warning(line, "Constant folded undefined addition generated NaN", "+");
        }
        else if (gl::isInf(result) && !gl::isInf(lhs) && !gl::isInf(rhs))
        {
            diag->warning(line, "Constant folded addition overflowed to infinity", "+");
        }
        return result;
    }
    
    float CheckedDiff(float lhs, float rhs, TDiagnostics *diag, const TSourceLoc &line)
    {
        float result = lhs - rhs;
        if (gl::isNaN(result) && !gl::isNaN(lhs) && !gl::isNaN(rhs))
        {
            diag->warning(line, "Constant folded undefined subtraction generated NaN", "-");
        }
        else if (gl::isInf(result) && !gl::isInf(lhs) && !gl::isInf(rhs))
        {
            diag->warning(line, "Constant folded subtraction overflowed to infinity", "-");
        }
        return result;
    }
    
    float CheckedMul(float lhs, float rhs, TDiagnostics *diag, const TSourceLoc &line)
    {
        float result = lhs * rhs;
        if (gl::isNaN(result) && !gl::isNaN(lhs) && !gl::isNaN(rhs))
        {
            diag->warning(line, "Constant folded undefined multiplication generated NaN", "*");
        }
        else if (gl::isInf(result) && !gl::isInf(lhs) && !gl::isInf(rhs))
        {
            diag->warning(line, "Constant folded multiplication overflowed to infinity", "*");
        }
        return result;
    }
    
    bool IsValidShiftOffset(const TConstantUnion &rhs)
    {
        return (rhs.getType() == EbtInt && (rhs.getIConst() >= 0 && rhs.getIConst() <= 31)) ||
               (rhs.getType() == EbtUInt && rhs.getUConst() <= 31u);
    }
    
    }  // anonymous namespace
    
    TConstantUnion::TConstantUnion()
    {
        iConst = 0;
        type   = EbtVoid;
    }
    
    int TConstantUnion::getIConst() const
    {
        ASSERT(type == EbtInt);
        return iConst;
    }
    
    unsigned int TConstantUnion::getUConst() const
    {
        ASSERT(type == EbtUInt);
        return uConst;
    }
    
    float TConstantUnion::getFConst() const
    {
        switch (type)
        {
            case EbtInt:
                return static_cast<float>(iConst);
            case EbtUInt:
                return static_cast<float>(uConst);
            default:
                ASSERT(type == EbtFloat);
                return fConst;
        }
    }
    
    bool TConstantUnion::getBConst() const
    {
        ASSERT(type == EbtBool);
        return bConst;
    }
    
    bool TConstantUnion::isZero() const
    {
        switch (type)
        {
            case EbtInt:
                return getIConst() == 0;
            case EbtUInt:
                return getUConst() == 0;
            case EbtFloat:
                return getFConst() == 0.0f;
            case EbtBool:
                return getBConst() == false;
            default:
                return false;
        }
    }
    
    TYuvCscStandardEXT TConstantUnion::getYuvCscStandardEXTConst() const
    {
        ASSERT(type == EbtYuvCscStandardEXT);
        return yuvCscStandardEXTConst;
    }
    
    bool TConstantUnion::cast(TBasicType newType, const TConstantUnion &constant)
    {
        switch (newType)
        {
            case EbtFloat:
                switch (constant.type)
                {
                    case EbtInt:
                        setFConst(static_cast<float>(constant.getIConst()));
                        break;
                    case EbtUInt:
                        setFConst(static_cast<float>(constant.getUConst()));
                        break;
                    case EbtBool:
                        setFConst(static_cast<float>(constant.getBConst()));
                        break;
                    case EbtFloat:
                        setFConst(static_cast<float>(constant.getFConst()));
                        break;
                    default:
                        return false;
                }
                break;
            case EbtInt:
                switch (constant.type)
                {
                    case EbtInt:
                        setIConst(static_cast<int>(constant.getIConst()));
                        break;
                    case EbtUInt:
                        setIConst(static_cast<int>(constant.getUConst()));
                        break;
                    case EbtBool:
                        setIConst(static_cast<int>(constant.getBConst()));
                        break;
                    case EbtFloat:
                        setIConst(static_cast<int>(constant.getFConst()));
                        break;
                    default:
                        return false;
                }
                break;
            case EbtUInt:
                switch (constant.type)
                {
                    case EbtInt:
                        setUConst(static_cast<unsigned int>(constant.getIConst()));
                        break;
                    case EbtUInt:
                        setUConst(static_cast<unsigned int>(constant.getUConst()));
                        break;
                    case EbtBool:
                        setUConst(static_cast<unsigned int>(constant.getBConst()));
                        break;
                    case EbtFloat:
                        if (constant.getFConst() < 0.0f)
                        {
                            // Avoid undefined behavior in C++ by first casting to signed int.
                            setUConst(
                                static_cast<unsigned int>(static_cast<int>(constant.getFConst())));
                        }
                        else
                        {
                            setUConst(static_cast<unsigned int>(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
    {
        switch (type)
        {
            case EbtFloat:
                return static_cast<float>(i) == fConst;
            default:
                return i == iConst;
        }
    }
    
    bool TConstantUnion::operator==(const unsigned int u) const
    {
        switch (type)
        {
            case EbtFloat:
                return static_cast<float>(u) == fConst;
            default:
                return u == uConst;
        }
    }
    
    bool TConstantUnion::operator==(const float f) const
    {
        switch (type)
        {
            case EbtInt:
                return f == static_cast<float>(iConst);
            case EbtUInt:
                return f == static_cast<float>(uConst);
            default:
                return f == fConst;
        }
    }
    
    bool TConstantUnion::operator==(const bool b) const
    {
        return b == bConst;
    }
    
    bool TConstantUnion::operator==(const TYuvCscStandardEXT s) const
    {
        return s == yuvCscStandardEXTConst;
    }
    
    bool TConstantUnion::operator==(const TConstantUnion &constant) const
    {
        ImplicitTypeConversion conversion = GetConversion(constant.type, type);
        if (conversion == ImplicitTypeConversion::Same)
        {
            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;
                case EbtYuvCscStandardEXT:
                    return constant.yuvCscStandardEXTConst == yuvCscStandardEXTConst;
                default:
                    return false;
            }
        }
        else if (conversion == ImplicitTypeConversion::Invalid)
        {
            return false;
        }
        else
        {
            return constant.getFConst() == getFConst();
        }
    }
    
    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 TYuvCscStandardEXT s) const
    {
        return !operator==(s);
    }
    
    bool TConstantUnion::operator!=(const TConstantUnion &constant) const
    {
        return !operator==(constant);
    }
    
    bool TConstantUnion::operator>(const TConstantUnion &constant) const
    {
    
        ImplicitTypeConversion conversion = GetConversion(constant.type, type);
        if (conversion == ImplicitTypeConversion::Same)
        {
            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
            }
        }
        else
        {
            ASSERT(conversion != ImplicitTypeConversion::Invalid);
            return getFConst() > constant.getFConst();
        }
    }
    
    bool TConstantUnion::operator<(const TConstantUnion &constant) const
    {
        ImplicitTypeConversion conversion = GetConversion(constant.type, type);
        if (conversion == ImplicitTypeConversion::Same)
        {
            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
            }
        }
        else
        {
            ASSERT(conversion != ImplicitTypeConversion::Invalid);
            return getFConst() < constant.getFConst();
        }
    }
    
    // static
    TConstantUnion TConstantUnion::add(const TConstantUnion &lhs,
                                       const TConstantUnion &rhs,
                                       TDiagnostics *diag,
                                       const TSourceLoc &line)
    {
        TConstantUnion returnValue;
    
        ImplicitTypeConversion conversion = GetConversion(lhs.type, rhs.type);
        if (conversion == ImplicitTypeConversion::Same)
        {
            switch (lhs.type)
            {
                case EbtInt:
                    returnValue.setIConst(gl::WrappingSum<int>(lhs.iConst, rhs.iConst));
                    break;
                case EbtUInt:
                    returnValue.setUConst(gl::WrappingSum<unsigned int>(lhs.uConst, rhs.uConst));
                    break;
                case EbtFloat:
                    returnValue.setFConst(CheckedSum(lhs.fConst, rhs.fConst, diag, line));
                    break;
                default:
                    UNREACHABLE();
            }
        }
        else
        {
            ASSERT(conversion != ImplicitTypeConversion::Invalid);
            returnValue.setFConst(CheckedSum(lhs.getFConst(), rhs.getFConst(), diag, line));
        }
    
        return returnValue;
    }
    
    // static
    TConstantUnion TConstantUnion::sub(const TConstantUnion &lhs,
                                       const TConstantUnion &rhs,
                                       TDiagnostics *diag,
                                       const TSourceLoc &line)
    {
        TConstantUnion returnValue;
    
        ImplicitTypeConversion conversion = GetConversion(lhs.type, rhs.type);
        if (conversion == ImplicitTypeConversion::Same)
        {
            switch (lhs.type)
            {
                case EbtInt:
                    returnValue.setIConst(gl::WrappingDiff<int>(lhs.iConst, rhs.iConst));
                    break;
                case EbtUInt:
                    returnValue.setUConst(gl::WrappingDiff<unsigned int>(lhs.uConst, rhs.uConst));
                    break;
                case EbtFloat:
                    returnValue.setFConst(CheckedDiff(lhs.fConst, rhs.fConst, diag, line));
                    break;
                default:
                    UNREACHABLE();
            }
        }
        else
        {
            ASSERT(conversion != ImplicitTypeConversion::Invalid);
            returnValue.setFConst(CheckedDiff(lhs.getFConst(), rhs.getFConst(), diag, line));
        }
    
        return returnValue;
    }
    
    // static
    TConstantUnion TConstantUnion::mul(const TConstantUnion &lhs,
                                       const TConstantUnion &rhs,
                                       TDiagnostics *diag,
                                       const TSourceLoc &line)
    {
        TConstantUnion returnValue;
    
        ImplicitTypeConversion conversion = GetConversion(lhs.type, rhs.type);
        if (conversion == ImplicitTypeConversion::Same)
        {
            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();
            }
        }
        else
        {
            ASSERT(conversion != ImplicitTypeConversion::Invalid);
            returnValue.setFConst(CheckedMul(lhs.getFConst(), rhs.getFConst(), diag, line));
        }
    
        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 (!IsValidShiftOffset(rhs))
        {
            diag->warning(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<unsigned int>(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<int>::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<int>(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 (!IsValidShiftOffset(rhs))
        {
            diag->warning(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<int>(static_cast<uint32_t>(lhs.iConst) << rhs.iConst));
                        break;
                    case EbtUInt:
                        returnValue.setIConst(
                            static_cast<int>(static_cast<uint32_t>(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