<commit_before>//-----------------------------------------------------------------------------
//  This is an implementation of Tom Forsyth's "Linear-Speed Vertex Cache
//  Optimization" algorithm as described here:
//  http://home.comcast.net/~tom_forsyth/papers/fast_vert_cache_opt.html
//
//  This code was authored and released into the public domain by
//  Adrian Stone (stone@gameangst.com).
//
//  THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
//  SHALL ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER
//  LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
//  IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//-----------------------------------------------------------------------------

#include <assert.h>
#include <math.h>
#include <vector>
#include <limits>
#include <algorithm>

namespace Forsyth
{
    typedef unsigned int uint;
    typedef unsigned short uint16;
    typedef unsigned char byte;

    //-----------------------------------------------------------------------------
    //  OptimizeFaces
    //-----------------------------------------------------------------------------
    //  Parameters:
    //      indexList
    //          input index list
    //      indexCount
    //          the number of indices in the list
    //      vertexCount
    //          the largest index value in indexList
    //      newIndexList
    //          a pointer to a preallocated buffer the same size as indexList to
    //          hold the optimized index list
    //      lruCacheSize
    //          the size of the simulated post-transform cache (max:64)
    //-----------------------------------------------------------------------------
    void OptimizeFaces(const uint16* indexList, uint indexCount, uint vertexCount, uint16* newIndexList, uint16 lruCacheSize);

    namespace
    {
        // code for computing vertex score was taken, as much as possible
        // directly from the original publication.
        float ComputeVertexCacheScore(int cachePosition, int vertexCacheSize)
        {
            const float FindVertexScore_CacheDecayPower = 1.5f;
            const float FindVertexScore_LastTriScore = 0.75f;

            float score = 0.0f;
            if ( cachePosition < 0 )
            {
                // Vertex is not in FIFO cache - no score.
            }
            else
            {
                if ( cachePosition < 3 )
                {
                    // This vertex was used in the last triangle,
                    // so it has a fixed score, whichever of the three
                    // it's in. Otherwise, you can get very different
                    // answers depending on whether you add
                    // the triangle 1,2,3 or 3,1,2 - which is silly.
                    score = FindVertexScore_LastTriScore;
                }
                else
                {
                    assert ( cachePosition < vertexCacheSize );
                    // Points for being high in the cache.
                    const float scaler = 1.0f / ( vertexCacheSize - 3 );
                    score = 1.0f - ( cachePosition - 3 ) * scaler;
                    score = powf ( score, FindVertexScore_CacheDecayPower );
                }
            }

            return score;
        }

        float ComputeVertexValenceScore(uint numActiveFaces)
        {
            const float FindVertexScore_ValenceBoostScale = 2.0f;
            const float FindVertexScore_ValenceBoostPower = 0.5f;

            float score = 0.f;

            // Bonus points for having a low number of tris still to
            // use the vert, so we get rid of lone verts quickly.
            float valenceBoost = powf ( static_cast<float>(numActiveFaces),
                -FindVertexScore_ValenceBoostPower );
            score += FindVertexScore_ValenceBoostScale * valenceBoost;

            return score;
        }

        const int kMaxVertexCacheSize = 64;
        const uint kMaxPrecomputedVertexValenceScores = 64;
        float s_vertexCacheScores[kMaxVertexCacheSize+1][kMaxVertexCacheSize];
        float s_vertexValenceScores[kMaxPrecomputedVertexValenceScores];

        bool ComputeVertexScores()
        {
            for (int cacheSize=0; cacheSize<=kMaxVertexCacheSize; ++cacheSize)
            {
                for (int cachePos=0; cachePos<cacheSize; ++cachePos)
                {
                    s_vertexCacheScores[cacheSize][cachePos] = ComputeVertexCacheScore(cachePos, cacheSize);
                }
            }

            for (uint valence=0; valence<kMaxPrecomputedVertexValenceScores; ++valence)
            {
                s_vertexValenceScores[valence] = ComputeVertexValenceScore(valence);
            }

            return true;
        }
        bool s_vertexScoresComputed = ComputeVertexScores();

//        inline float FindVertexCacheScore(uint cachePosition, uint maxSizeVertexCache)
//        {
//            return s_vertexCacheScores[maxSizeVertexCache][cachePosition];
//        }

//        inline float FindVertexValenceScore(uint numActiveTris)
//        {
//            return s_vertexValenceScores[numActiveTris];
//        }

        float FindVertexScore(uint numActiveFaces, uint cachePosition, uint vertexCacheSize)
        {
            assert(s_vertexScoresComputed);

            if ( numActiveFaces == 0 )
            {
                // No tri needs this vertex!
                return -1.0f;
            }

            float score = 0.f;
            if (cachePosition < vertexCacheSize)
            {
                score += s_vertexCacheScores[vertexCacheSize][cachePosition];
            }

            if (numActiveFaces < kMaxPrecomputedVertexValenceScores)
            {
                score += s_vertexValenceScores[numActiveFaces];
            }
            else
            {
                score += ComputeVertexValenceScore(numActiveFaces);
            }

            return score;
        }

        struct OptimizeVertexData
        {
            float   score;
            uint    activeFaceListStart;
            uint    activeFaceListSize;
            uint16  cachePos0;
            uint16  cachePos1;
            OptimizeVertexData() : score(0.f), activeFaceListStart(0), activeFaceListSize(0), cachePos0(0), cachePos1(0) { }
        };
    }

    void OptimizeFaces(const uint16* indexList, uint indexCount, uint vertexCount, uint16* newIndexList, uint16 lruCacheSize)
    {
        std::vector<OptimizeVertexData> vertexDataList;
        vertexDataList.resize(vertexCount);

        // compute face count per vertex
        for (uint i=0; i<indexCount; ++i)
        {
            uint16 index = indexList[i];
            assert(index < vertexCount);
            OptimizeVertexData& vertexData = vertexDataList[index];
            vertexData.activeFaceListSize++;
        }

        std::vector<uint> activeFaceList;

        const uint16 kEvictedCacheIndex = std::numeric_limits<uint16>::max();

        {
            // allocate face list per vertex
            uint curActiveFaceListPos = 0;
            for (uint i=0; i<vertexCount; ++i)
            {
                OptimizeVertexData& vertexData = vertexDataList[i];
                vertexData.cachePos0 = kEvictedCacheIndex;
                vertexData.cachePos1 = kEvictedCacheIndex;
                vertexData.activeFaceListStart = curActiveFaceListPos;
                curActiveFaceListPos += vertexData.activeFaceListSize;
                vertexData.score = FindVertexScore(vertexData.activeFaceListSize, vertexData.cachePos0, lruCacheSize);
                vertexData.activeFaceListSize = 0;
            }
            activeFaceList.resize(curActiveFaceListPos);
        }

        // fill out face list per vertex
        for (uint i=0; i<indexCount; i+=3)
        {
            for (uint j=0; j<3; ++j)
            {
                uint16 index = indexList[i+j];
                OptimizeVertexData& vertexData = vertexDataList[index];
                activeFaceList[vertexData.activeFaceListStart + vertexData.activeFaceListSize] = i;
                vertexData.activeFaceListSize++;
            }
        }

        std::vector<byte> processedFaceList;
        processedFaceList.resize(indexCount);

        uint16 vertexCacheBuffer[(kMaxVertexCacheSize+3)*2];
        uint16* cache0 = vertexCacheBuffer;
        uint16* cache1 = vertexCacheBuffer+(kMaxVertexCacheSize+3);
        uint16 entriesInCache0 = 0;

        uint bestFace = 0;
        float bestScore = -1.f;

        const float maxValenceScore = FindVertexScore(1, kEvictedCacheIndex, lruCacheSize) * 3.f;

        for (uint i = 0; i < indexCount; i += 3)
        {
            if (bestScore < 0.f)
            {
                // no verts in the cache are used by any unprocessed faces so
                // search all unprocessed faces for a new starting point
                for (uint j = 0; j < indexCount; j += 3)
                {
                    if (processedFaceList[j] == 0)
                    {
                        uint face = j;
                        float faceScore = 0.f;
                        for (uint k=0; k<3; ++k)
                        {
                            uint16 index = indexList[face+k];
                            OptimizeVertexData& vertexData = vertexDataList[index];
                            assert(vertexData.activeFaceListSize > 0);
                            assert(vertexData.cachePos0 >= lruCacheSize);
                            faceScore += vertexData.score;
                        }

                        if (faceScore > bestScore)
                        {
                            bestScore = faceScore;
                            bestFace = face;

                            assert(bestScore <= maxValenceScore);
                            if (bestScore >= maxValenceScore)
                            {
                                break;
                            }
                        }
                    }
                }
                assert(bestScore >= 0.f);
            }

            processedFaceList[bestFace] = 1;
            uint16 entriesInCache1 = 0;

            // add bestFace to LRU cache and to newIndexList
            for (uint v = 0; v < 3; ++v)
            {
                uint16 index = indexList[bestFace+v];
                newIndexList[i+v] = index;

                OptimizeVertexData& vertexData = vertexDataList[index];

                if (vertexData.cachePos1 >= entriesInCache1)
                {
                    vertexData.cachePos1 = entriesInCache1;
                    cache1[entriesInCache1++] = index;

                    if (vertexData.activeFaceListSize == 1)
                    {
                        --vertexData.activeFaceListSize;
                        continue;
                    }
                }

                assert(vertexData.activeFaceListSize > 0);
                uint* begin = &activeFaceList[vertexData.activeFaceListStart];
                uint* end = &(activeFaceList[vertexData.activeFaceListStart + vertexData.activeFaceListSize - 1]) + 1;
                uint* it = std::find(begin, end, bestFace);
                assert(it != end);
                std::swap(*it, *(end-1));
                --vertexData.activeFaceListSize;
                vertexData.score = FindVertexScore(vertexData.activeFaceListSize, vertexData.cachePos1, lruCacheSize);

            }

            // move the rest of the old verts in the cache down and compute their new scores
            for (uint c0 = 0; c0 < entriesInCache0; ++c0)
            {
                uint16 index = cache0[c0];
                OptimizeVertexData& vertexData = vertexDataList[index];

                if (vertexData.cachePos1 >= entriesInCache1)
                {
                    vertexData.cachePos1 = entriesInCache1;
                    cache1[entriesInCache1++] = index;
                    vertexData.score = FindVertexScore(vertexData.activeFaceListSize, vertexData.cachePos1, lruCacheSize);
                }
            }

            // find the best scoring triangle in the current cache (including up to 3 that were just evicted)
            bestScore = -1.f;
            for (uint c1 = 0; c1 < entriesInCache1; ++c1)
            {
                uint16 index = cache1[c1];
                OptimizeVertexData& vertexData = vertexDataList[index];
                vertexData.cachePos0 = vertexData.cachePos1;
                vertexData.cachePos1 = kEvictedCacheIndex;
                for (uint j=0; j<vertexData.activeFaceListSize; ++j)
                {
                    uint face = activeFaceList[vertexData.activeFaceListStart+j];
                    float faceScore = 0.f;
                    for (uint v=0; v<3; v++)
                    {
                        uint16 faceIndex = indexList[face+v];
                        OptimizeVertexData& faceVertexData = vertexDataList[faceIndex];
                        faceScore += faceVertexData.score;
                    }
                    if (faceScore > bestScore)
                    {
                        bestScore = faceScore;
                        bestFace = face;
                    }
                }
            }

            std::swap(cache0, cache1);
            entriesInCache0 = std::min(entriesInCache1, lruCacheSize);
        }
    }

} // namespace Forsyth
<commit_msg>Fixed unused variable.<commit_after>//-----------------------------------------------------------------------------
//  This is an implementation of Tom Forsyth's "Linear-Speed Vertex Cache
//  Optimization" algorithm as described here:
//  http://home.comcast.net/~tom_forsyth/papers/fast_vert_cache_opt.html
//
//  This code was authored and released into the public domain by
//  Adrian Stone (stone@gameangst.com).
//
//  THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
//  SHALL ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER
//  LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
//  IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//-----------------------------------------------------------------------------

#include <assert.h>
#include <math.h>
#include <vector>
#include <limits>
#include <algorithm>

namespace Forsyth
{
    typedef unsigned int uint;
    typedef unsigned short uint16;
    typedef unsigned char byte;

    //-----------------------------------------------------------------------------
    //  OptimizeFaces
    //-----------------------------------------------------------------------------
    //  Parameters:
    //      indexList
    //          input index list
    //      indexCount
    //          the number of indices in the list
    //      vertexCount
    //          the largest index value in indexList
    //      newIndexList
    //          a pointer to a preallocated buffer the same size as indexList to
    //          hold the optimized index list
    //      lruCacheSize
    //          the size of the simulated post-transform cache (max:64)
    //-----------------------------------------------------------------------------
    void OptimizeFaces(const uint16* indexList, uint indexCount, uint vertexCount, uint16* newIndexList, uint16 lruCacheSize);

    namespace
    {
        // code for computing vertex score was taken, as much as possible
        // directly from the original publication.
        float ComputeVertexCacheScore(int cachePosition, int vertexCacheSize)
        {
            const float FindVertexScore_CacheDecayPower = 1.5f;
            const float FindVertexScore_LastTriScore = 0.75f;

            float score = 0.0f;
            if ( cachePosition < 0 )
            {
                // Vertex is not in FIFO cache - no score.
            }
            else
            {
                if ( cachePosition < 3 )
                {
                    // This vertex was used in the last triangle,
                    // so it has a fixed score, whichever of the three
                    // it's in. Otherwise, you can get very different
                    // answers depending on whether you add
                    // the triangle 1,2,3 or 3,1,2 - which is silly.
                    score = FindVertexScore_LastTriScore;
                }
                else
                {
                    assert ( cachePosition < vertexCacheSize );
                    // Points for being high in the cache.
                    const float scaler = 1.0f / ( vertexCacheSize - 3 );
                    score = 1.0f - ( cachePosition - 3 ) * scaler;
                    score = powf ( score, FindVertexScore_CacheDecayPower );
                }
            }

            return score;
        }

        float ComputeVertexValenceScore(uint numActiveFaces)
        {
            const float FindVertexScore_ValenceBoostScale = 2.0f;
            const float FindVertexScore_ValenceBoostPower = 0.5f;

            float score = 0.f;

            // Bonus points for having a low number of tris still to
            // use the vert, so we get rid of lone verts quickly.
            float valenceBoost = powf ( static_cast<float>(numActiveFaces),
                -FindVertexScore_ValenceBoostPower );
            score += FindVertexScore_ValenceBoostScale * valenceBoost;

            return score;
        }

        const int kMaxVertexCacheSize = 64;
        const uint kMaxPrecomputedVertexValenceScores = 64;
        float s_vertexCacheScores[kMaxVertexCacheSize+1][kMaxVertexCacheSize];
        float s_vertexValenceScores[kMaxPrecomputedVertexValenceScores];

        bool ComputeVertexScores()
        {
            for (int cacheSize=0; cacheSize<=kMaxVertexCacheSize; ++cacheSize)
            {
                for (int cachePos=0; cachePos<cacheSize; ++cachePos)
                {
                    s_vertexCacheScores[cacheSize][cachePos] = ComputeVertexCacheScore(cachePos, cacheSize);
                }
            }

            for (uint valence=0; valence<kMaxPrecomputedVertexValenceScores; ++valence)
            {
                s_vertexValenceScores[valence] = ComputeVertexValenceScore(valence);
            }

            return true;
        }
        bool s_vertexScoresComputed = ComputeVertexScores();

//        inline float FindVertexCacheScore(uint cachePosition, uint maxSizeVertexCache)
//        {
//            return s_vertexCacheScores[maxSizeVertexCache][cachePosition];
//        }

//        inline float FindVertexValenceScore(uint numActiveTris)
//        {
//            return s_vertexValenceScores[numActiveTris];
//        }

        float FindVertexScore(uint numActiveFaces, uint cachePosition, uint vertexCacheSize)
        {
            assert(s_vertexScoresComputed); (void)s_vertexScoresComputed;

            if ( numActiveFaces == 0 )
            {
                // No tri needs this vertex!
                return -1.0f;
            }

            float score = 0.f;
            if (cachePosition < vertexCacheSize)
            {
                score += s_vertexCacheScores[vertexCacheSize][cachePosition];
            }

            if (numActiveFaces < kMaxPrecomputedVertexValenceScores)
            {
                score += s_vertexValenceScores[numActiveFaces];
            }
            else
            {
                score += ComputeVertexValenceScore(numActiveFaces);
            }

            return score;
        }

        struct OptimizeVertexData
        {
            float   score;
            uint    activeFaceListStart;
            uint    activeFaceListSize;
            uint16  cachePos0;
            uint16  cachePos1;
            OptimizeVertexData() : score(0.f), activeFaceListStart(0), activeFaceListSize(0), cachePos0(0), cachePos1(0) { }
        };
    }

    void OptimizeFaces(const uint16* indexList, uint indexCount, uint vertexCount, uint16* newIndexList, uint16 lruCacheSize)
    {
        std::vector<OptimizeVertexData> vertexDataList;
        vertexDataList.resize(vertexCount);

        // compute face count per vertex
        for (uint i=0; i<indexCount; ++i)
        {
            uint16 index = indexList[i];
            assert(index < vertexCount);
            OptimizeVertexData& vertexData = vertexDataList[index];
            vertexData.activeFaceListSize++;
        }

        std::vector<uint> activeFaceList;

        const uint16 kEvictedCacheIndex = std::numeric_limits<uint16>::max();

        {
            // allocate face list per vertex
            uint curActiveFaceListPos = 0;
            for (uint i=0; i<vertexCount; ++i)
            {
                OptimizeVertexData& vertexData = vertexDataList[i];
                vertexData.cachePos0 = kEvictedCacheIndex;
                vertexData.cachePos1 = kEvictedCacheIndex;
                vertexData.activeFaceListStart = curActiveFaceListPos;
                curActiveFaceListPos += vertexData.activeFaceListSize;
                vertexData.score = FindVertexScore(vertexData.activeFaceListSize, vertexData.cachePos0, lruCacheSize);
                vertexData.activeFaceListSize = 0;
            }
            activeFaceList.resize(curActiveFaceListPos);
        }

        // fill out face list per vertex
        for (uint i=0; i<indexCount; i+=3)
        {
            for (uint j=0; j<3; ++j)
            {
                uint16 index = indexList[i+j];
                OptimizeVertexData& vertexData = vertexDataList[index];
                activeFaceList[vertexData.activeFaceListStart + vertexData.activeFaceListSize] = i;
                vertexData.activeFaceListSize++;
            }
        }

        std::vector<byte> processedFaceList;
        processedFaceList.resize(indexCount);

        uint16 vertexCacheBuffer[(kMaxVertexCacheSize+3)*2];
        uint16* cache0 = vertexCacheBuffer;
        uint16* cache1 = vertexCacheBuffer+(kMaxVertexCacheSize+3);
        uint16 entriesInCache0 = 0;

        uint bestFace = 0;
        float bestScore = -1.f;

        const float maxValenceScore = FindVertexScore(1, kEvictedCacheIndex, lruCacheSize) * 3.f;

        for (uint i = 0; i < indexCount; i += 3)
        {
            if (bestScore < 0.f)
            {
                // no verts in the cache are used by any unprocessed faces so
                // search all unprocessed faces for a new starting point
                for (uint j = 0; j < indexCount; j += 3)
                {
                    if (processedFaceList[j] == 0)
                    {
                        uint face = j;
                        float faceScore = 0.f;
                        for (uint k=0; k<3; ++k)
                        {
                            uint16 index = indexList[face+k];
                            OptimizeVertexData& vertexData = vertexDataList[index];
                            assert(vertexData.activeFaceListSize > 0);
                            assert(vertexData.cachePos0 >= lruCacheSize);
                            faceScore += vertexData.score;
                        }

                        if (faceScore > bestScore)
                        {
                            bestScore = faceScore;
                            bestFace = face;

                            assert(bestScore <= maxValenceScore);
                            if (bestScore >= maxValenceScore)
                            {
                                break;
                            }
                        }
                    }
                }
                assert(bestScore >= 0.f);
            }

            processedFaceList[bestFace] = 1;
            uint16 entriesInCache1 = 0;

            // add bestFace to LRU cache and to newIndexList
            for (uint v = 0; v < 3; ++v)
            {
                uint16 index = indexList[bestFace+v];
                newIndexList[i+v] = index;

                OptimizeVertexData& vertexData = vertexDataList[index];

                if (vertexData.cachePos1 >= entriesInCache1)
                {
                    vertexData.cachePos1 = entriesInCache1;
                    cache1[entriesInCache1++] = index;

                    if (vertexData.activeFaceListSize == 1)
                    {
                        --vertexData.activeFaceListSize;
                        continue;
                    }
                }

                assert(vertexData.activeFaceListSize > 0);
                uint* begin = &activeFaceList[vertexData.activeFaceListStart];
                uint* end = &(activeFaceList[vertexData.activeFaceListStart + vertexData.activeFaceListSize - 1]) + 1;
                uint* it = std::find(begin, end, bestFace);
                assert(it != end);
                std::swap(*it, *(end-1));
                --vertexData.activeFaceListSize;
                vertexData.score = FindVertexScore(vertexData.activeFaceListSize, vertexData.cachePos1, lruCacheSize);

            }

            // move the rest of the old verts in the cache down and compute their new scores
            for (uint c0 = 0; c0 < entriesInCache0; ++c0)
            {
                uint16 index = cache0[c0];
                OptimizeVertexData& vertexData = vertexDataList[index];

                if (vertexData.cachePos1 >= entriesInCache1)
                {
                    vertexData.cachePos1 = entriesInCache1;
                    cache1[entriesInCache1++] = index;
                    vertexData.score = FindVertexScore(vertexData.activeFaceListSize, vertexData.cachePos1, lruCacheSize);
                }
            }

            // find the best scoring triangle in the current cache (including up to 3 that were just evicted)
            bestScore = -1.f;
            for (uint c1 = 0; c1 < entriesInCache1; ++c1)
            {
                uint16 index = cache1[c1];
                OptimizeVertexData& vertexData = vertexDataList[index];
                vertexData.cachePos0 = vertexData.cachePos1;
                vertexData.cachePos1 = kEvictedCacheIndex;
                for (uint j=0; j<vertexData.activeFaceListSize; ++j)
                {
                    uint face = activeFaceList[vertexData.activeFaceListStart+j];
                    float faceScore = 0.f;
                    for (uint v=0; v<3; v++)
                    {
                        uint16 faceIndex = indexList[face+v];
                        OptimizeVertexData& faceVertexData = vertexDataList[faceIndex];
                        faceScore += faceVertexData.score;
                    }
                    if (faceScore > bestScore)
                    {
                        bestScore = faceScore;
                        bestFace = face;
                    }
                }
            }

            std::swap(cache0, cache1);
            entriesInCache0 = std::min(entriesInCache1, lruCacheSize);
        }
    }

} // namespace Forsyth
<|endoftext|>


<commit_before>/*****************************************************************************
 * Project: BaBar detector at the SLAC PEP-II B-factory
 * Package: RooFitCore
 *    File: $Id: RooCurve.cc,v 1.27 2001/11/19 18:03:20 verkerke Exp $
 * Authors:
 *   DK, David Kirkby, Stanford University, kirkby@hep.stanford.edu
 * History:
 *   29-Apr-2001 DK Created initial version
 *
 * Copyright (C) 2001 Stanford University
 *****************************************************************************/

// -- CLASS DESCRIPTION [PLOT] --
// A RooCurve is a one-dimensional graphical representation of a real-valued function.
// A curve is approximated by straight line segments with endpoints chosen to give
// a "good" approximation to the true curve. The goodness of the approximation is
// controlled by a precision and a resolution parameter. To view the points where
// a function y(x) is actually evaluated to approximate a smooth curve, use:
//
//  RooPlot *p= y.plotOn(x.frame());
//  p->getAttMarker("curve_y")->SetMarkerStyle(20);
//  p->setDrawOptions("curve_y","PL");
//  p->Draw();

// #include "BaBar/BaBar.hh"

#include "RooFitCore/RooCurve.hh"
#include "RooFitCore/RooHist.hh"
#include "RooFitCore/RooAbsReal.hh"
#include "RooFitCore/RooArgSet.hh"
#include "RooFitCore/RooRealVar.hh"
#include "RooFitCore/RooRealIntegral.hh"
#include "RooFitCore/RooRealBinding.hh"
#include "RooFitCore/RooScaledFunc.hh"

#include <iostream.h>
#include <iomanip.h>
#include <math.h>
#include <assert.h>

ClassImp(RooCurve)

static const char rcsid[] =
"$Id: RooCurve.cc,v 1.27 2001/11/19 18:03:20 verkerke Exp $";

RooCurve::RooCurve() {
  initialize();
}

RooCurve::RooCurve(const RooAbsReal &f, RooAbsRealLValue &x, Double_t xlo, Double_t xhi, Int_t xbins,
		   Double_t scaleFactor, const RooArgSet *normVars, Double_t prec, Double_t resolution,
		   Bool_t shiftToZero) {
  // Create a 1-dim curve of the value of the specified real-valued expression
  // as a function of x. Use the optional precision parameter to control
  // how precisely the smooth curve is rasterized. Use the optional argument set
  // to specify how the expression should be normalized. Use the optional scale
  // factor to rescale the expression after normalization.
  // If shiftToZero is set, the entire curve is shift down to make the lowest
  // point in of the curve go through zero.

  // grab the function's name and title
  TString name("curve_");
  name.Append(f.GetName());
  SetName(name.Data());
  TString title(f.GetTitle());
  SetTitle(title.Data());
  // append " ( [<funit> ][/ <xunit> ])" to our y-axis label if necessary
  if(0 != strlen(f.getUnit()) || 0 != strlen(x.getUnit())) {
    title.Append(" ( ");
    if(0 != strlen(f.getUnit())) {
      title.Append(f.getUnit());
      title.Append(" ");
    }
    if(0 != strlen(x.getUnit())) {
      title.Append("/ ");
      title.Append(x.getUnit());
      title.Append(" ");
    }
    title.Append(")");
  }
  setYAxisLabel(title.Data());

  RooAbsFunc *funcPtr(0),*rawPtr(0);
  funcPtr= f.bindVars(x,normVars,kTRUE);

  // apply a scale factor if necessary
  if(scaleFactor != 1) {
    rawPtr= funcPtr;
    funcPtr= new RooScaledFunc(*rawPtr,scaleFactor);
  }
  assert(0 != funcPtr);

  // calculate the points to add to our curve
  Double_t prevYMax = getYAxisMax() ;
  addPoints(*funcPtr,xlo,xhi,xbins+1,prec,resolution);
  initialize();

  // cleanup
  delete funcPtr;
  if(rawPtr) delete rawPtr;
  if (shiftToZero) shiftCurveToZero(prevYMax) ;
}

RooCurve::RooCurve(const char *name, const char *title, const RooAbsFunc &func,
		   Double_t xlo, Double_t xhi, UInt_t minPoints, Double_t prec, Double_t resolution,
		   Bool_t shiftToZero) {
  SetName(name);
  SetTitle(title);
  Double_t prevYMax = getYAxisMax() ;
  addPoints(func,xlo,xhi,minPoints,prec,resolution);
  initialize();
  if (shiftToZero) shiftCurveToZero(prevYMax) ;
}

RooCurve::~RooCurve()
{
}

void RooCurve::initialize()
{
  // Perform initialization that is common to all constructors.

  // set default line width in pixels
  SetLineWidth(3);
  // set default line color
  SetLineColor(kBlue);
}

void RooCurve::shiftCurveToZero(Double_t prevYMax)
  // Find lowest point in curve and move all points in curve so that
  // lowest point will go exactly through zero
{
  Int_t i ;
  Double_t minVal(1e30) ;
  Double_t maxVal(-1e30) ;

  // First iteration, find current lowest point
  for (i=0 ; i<GetN() ; i++) {
    Double_t x,y ;
    GetPoint(i,x,y) ;
    if (y<minVal) minVal=y ;
    if (y>maxVal) maxVal=y ;
  }

  // Second iteration, lower all points by minVal
  for (i=0 ; i<GetN() ; i++) {
    Double_t x,y ;
    GetPoint(i,x,y) ;
    SetPoint(i,x,y-minVal) ;
  }

  // Check if y-axis range needs readjustment
  if (getYAxisMax()>prevYMax) {
    Double_t newMax = maxVal - minVal ;
    setYAxisLimits(getYAxisMin(), newMax<prevYMax ? prevYMax : newMax) ;
  }
}

void RooCurve::addPoints(const RooAbsFunc &func, Double_t xlo, Double_t xhi,
			 Int_t minPoints, Double_t prec, Double_t resolution) {
  // Add points calculated with the specified function, over the range (xlo,xhi).
  // Add at least minPoints equally spaced points, and add sufficient points so that
  // the maximum deviation from the final straight-line segements is prec*(ymax-ymin),
  // down to a minimum horizontal spacing of resolution*(xhi-xlo).

  // check the inputs
  if(!func.isValid()) {
    cout << fName << "::addPoints: input function is not valid" << endl;
    return;
  }
  if(minPoints <= 0 || xhi <= xlo) {
    cout << fName << "::addPoints: bad input (nothing added)" << endl;
    return;
  }

  // Perform a coarse scan of the function to estimate its y range.
  // Save the results so we do not have to re-evaluate at the scan points.
  Double_t *yval= new Double_t[minPoints];
  assert(0 != yval);
  Double_t x,dx= (xhi-xlo)/(minPoints-1.);
  for(Int_t step= 0; step < minPoints; step++) {
    x= xlo + step*dx;
    if (step==minPoints-1) x-=1e-15 ;
    yval[step]= func(&x);
    updateYAxisLimits(yval[step]);
  }

  // store points of the coarse scan and calculate any refinements necessary
  Double_t minDx= resolution*(xhi-xlo);
  Double_t x1,x2= xlo;

  addPoint(xlo-dx,0) ;
  addPoint(xlo-dx,yval[0]) ;

  addPoint(xlo,yval[0]);
  for(Int_t step= 1; step < minPoints; step++) {
    x1= x2;
    x2= xlo + step*dx;
    addRange(func,x1,x2,yval[step-1],yval[step],prec,minDx);
  }
  addPoint(xhi,yval[minPoints-1]) ;

  addPoint(xhi+dx,yval[minPoints-1]) ;
  addPoint(xhi+dx,0) ;

  // cleanup
  delete [] yval;
}

void RooCurve::addRange(const RooAbsFunc& func, Double_t x1, Double_t x2,
			Double_t y1, Double_t y2, Double_t prec, Double_t minDx) {
  // Fill the range (x1,x2) with points calculated using func(&x). No point will
  // be added at x1, and a point will always be added at x2. The density of points
  // will be calculated so that the maximum deviation from a straight line
  // approximation is prec*(ymax-ymin) down to the specified minimum horizontal spacing.

  // calculate our value at the midpoint of this range
  Double_t xmid= 0.5*(x1+x2);
  Double_t ymid= func(&xmid);
  // test if the midpoint is sufficiently close to a straight line across this interval
  Double_t dy= ymid - 0.5*(y1+y2);
  if((xmid - x1 >= minDx) && fabs(dy)>0 && fabs(dy) >= prec*(getYAxisMax()-getYAxisMin())) {
    // fill in each subrange
    updateYAxisLimits(ymid);
    addRange(func,x1,xmid,y1,ymid,prec,minDx);
    addRange(func,xmid,x2,ymid,y2,prec,minDx);
  }
  else {
    // add the endpoint
    addPoint(x2,y2);
  }
}

void RooCurve::addPoint(Double_t x, Double_t y) {
  // Add a point with the specified coordinates. Update our y-axis limits.

  // cout << "RooCurve("<< GetName() << ") adding point at (" << x << "," << y << ")" << endl ;
  Int_t next= GetN();
  SetPoint(next, x, y);
}

Double_t RooCurve::getFitRangeNEvt() const {
  return 1;
}

Double_t RooCurve::getFitRangeBinW() const {
  return 1;
}

void RooCurve::printToStream(ostream& os, PrintOption opt, TString indent) const {
  // Print info about this histogram to the specified output stream.
  //
  //   Standard: number of entries
  //    Verbose: print points on curve

  oneLinePrint(os,*this);
  RooPlotable::printToStream(os,opt,indent);
  if(opt >= Standard) {
    os << indent << "--- RooCurve ---" << endl;
    Int_t n= GetN();
    os << indent << "  Contains " << n << " points" << endl;
    if(opt >= Verbose) {
      os << indent << "  Graph points:" << endl;
      for(Int_t i= 0; i < n; i++) {
	os << indent << setw(3) << i << ") x = " << fX[i] << " , y = " << fY[i] << endl;
      }
    }
  }
}

Double_t RooCurve::chiSquare(const RooHist& hist) const
{
  Int_t i,np = hist.GetN() ;
  Double_t x,y,eyl,eyh ;
  Double_t hbinw2 = hist.getNominalBinWidth()/2 ;

  Double_t chisq(0) ;
  for (i=0 ; i<np ; i++) {
    // Retrieve histogram contents
    ((RooHist&)hist).GetPoint(i,x,y) ;
    eyl = hist.GetEYlow()[i] ;
    eyh = hist.GetEYhigh()[i] ;

    // Integrate function over this bin
    Double_t avg = average(x-hbinw2,x+hbinw2) ;

    // Add pull^2 to chisq
    if (y!=0) {
      Double_t pull = (y>avg) ? ((y-avg)/eyh) : ((y-avg)/eyl) ;
      chisq += pull*pull ;
    }
  }

  // Return chisq/nDOF
  return chisq / np ;
}

Double_t RooCurve::average(Double_t lo, Double_t hi) const
{
  // Find points corresponding to first and last point
  Int_t ifirst = findPoint(lo) ;
  Int_t ilast  = findPoint(hi) ;

  if (ilast<=ifirst) {
    cout << "RooCurve::average(" << GetName()
	 << ") invalid range (" << lo << "," << hi << ")" << endl ;
    return 0 ;
  }

  // Trapezoid integration
  Int_t i ;
  Double_t sum(0),x1,y1,x2,y2 ;
  for (i=ifirst ; i<ilast ; i++) {
    ((RooCurve&)*this).GetPoint(i,x1,y1) ;
    ((RooCurve&)*this).GetPoint(i+1,x2,y2) ;
    sum += (x2-x1)*(y1+y2)/2 ;
  }

  // Return trapezoid sum devided by integration range
  ((RooCurve&)*this).GetPoint(ifirst,x1,y1) ;
  ((RooCurve&)*this).GetPoint(ilast,x2,y2) ;
  return sum/(x2-x1) ;
}

Int_t RooCurve::findPoint(Double_t xvalue) const
{
  Double_t delta(999.),x,y ;
  Int_t i,n = GetN() ;
  Int_t ibest(-1) ;
  for (i=0 ; i<n ; i++) {
    ((RooCurve&)*this).GetPoint(i,x,y) ;
    if (fabs(xvalue-x)<delta) {
      delta = fabs(xvalue-x) ;
      ibest = i ;
    }
  }

  return (delta<1e-10)?ibest:-1 ;
}
<commit_msg><commit_after>/*****************************************************************************
 * Project: BaBar detector at the SLAC PEP-II B-factory
 * Package: RooFitCore
 *    File: $Id: RooCurve.cc,v 1.28 2001/12/13 23:08:44 verkerke Exp $
 * Authors:
 *   DK, David Kirkby, Stanford University, kirkby@hep.stanford.edu
 * History:
 *   29-Apr-2001 DK Created initial version
 *
 * Copyright (C) 2001 Stanford University
 *****************************************************************************/

// -- CLASS DESCRIPTION [PLOT] --
// A RooCurve is a one-dimensional graphical representation of a real-valued function.
// A curve is approximated by straight line segments with endpoints chosen to give
// a "good" approximation to the true curve. The goodness of the approximation is
// controlled by a precision and a resolution parameter. To view the points where
// a function y(x) is actually evaluated to approximate a smooth curve, use:
//
//  RooPlot *p= y.plotOn(x.frame());
//  p->getAttMarker("curve_y")->SetMarkerStyle(20);
//  p->setDrawOptions("curve_y","PL");
//  p->Draw();

// #include "BaBar/BaBar.hh"

#include "RooFitCore/RooCurve.hh"
#include "RooFitCore/RooHist.hh"
#include "RooFitCore/RooAbsReal.hh"
#include "RooFitCore/RooArgSet.hh"
#include "RooFitCore/RooRealVar.hh"
#include "RooFitCore/RooRealIntegral.hh"
#include "RooFitCore/RooRealBinding.hh"
#include "RooFitCore/RooScaledFunc.hh"

#include <iostream.h>
#include <iomanip.h>
#include <math.h>
#include <assert.h>

ClassImp(RooCurve)

static const char rcsid[] =
"$Id: RooCurve.cc,v 1.28 2001/12/13 23:08:44 verkerke Exp $";

RooCurve::RooCurve() {
  initialize();
}

RooCurve::RooCurve(const RooAbsReal &f, RooAbsRealLValue &x, Double_t xlo, Double_t xhi, Int_t xbins,
		   Double_t scaleFactor, const RooArgSet *normVars, Double_t prec, Double_t resolution,
		   Bool_t shiftToZero) {
  // Create a 1-dim curve of the value of the specified real-valued expression
  // as a function of x. Use the optional precision parameter to control
  // how precisely the smooth curve is rasterized. Use the optional argument set
  // to specify how the expression should be normalized. Use the optional scale
  // factor to rescale the expression after normalization.
  // If shiftToZero is set, the entire curve is shift down to make the lowest
  // point in of the curve go through zero.

  // grab the function's name and title
  TString name("curve_");
  name.Append(f.GetName());
  SetName(name.Data());
  TString title(f.GetTitle());
  SetTitle(title.Data());
  // append " ( [<funit> ][/ <xunit> ])" to our y-axis label if necessary
  if(0 != strlen(f.getUnit()) || 0 != strlen(x.getUnit())) {
    title.Append(" ( ");
    if(0 != strlen(f.getUnit())) {
      title.Append(f.getUnit());
      title.Append(" ");
    }
    if(0 != strlen(x.getUnit())) {
      title.Append("/ ");
      title.Append(x.getUnit());
      title.Append(" ");
    }
    title.Append(")");
  }
  setYAxisLabel(title.Data());

  RooAbsFunc *funcPtr(0),*rawPtr(0);
  funcPtr= f.bindVars(x,normVars,kTRUE);

  // apply a scale factor if necessary
  if(scaleFactor != 1) {
    rawPtr= funcPtr;
    funcPtr= new RooScaledFunc(*rawPtr,scaleFactor);
  }
  assert(0 != funcPtr);

  // calculate the points to add to our curve
  Double_t prevYMax = getYAxisMax() ;
  addPoints(*funcPtr,xlo,xhi,xbins+1,prec,resolution);
  initialize();

  // cleanup
  delete funcPtr;
  if(rawPtr) delete rawPtr;
  if (shiftToZero) shiftCurveToZero(prevYMax) ;
}

RooCurve::RooCurve(const char *name, const char *title, const RooAbsFunc &func,
		   Double_t xlo, Double_t xhi, UInt_t minPoints, Double_t prec, Double_t resolution,
		   Bool_t shiftToZero) {
  SetName(name);
  SetTitle(title);
  Double_t prevYMax = getYAxisMax() ;
  addPoints(func,xlo,xhi,minPoints,prec,resolution);
  initialize();
  if (shiftToZero) shiftCurveToZero(prevYMax) ;
}

RooCurve::~RooCurve()
{
}

void RooCurve::initialize()
{
  // Perform initialization that is common to all constructors.

  // set default line width in pixels
  SetLineWidth(3);
  // set default line color
  SetLineColor(kBlue);
}

void RooCurve::shiftCurveToZero(Double_t prevYMax)
  // Find lowest point in curve and move all points in curve so that
  // lowest point will go exactly through zero
{
  Int_t i ;
  Double_t minVal(1e30) ;
  Double_t maxVal(-1e30) ;

  // First iteration, find current lowest point
  for (i=1 ; i<GetN()-1 ; i++) {
    Double_t x,y ;
    GetPoint(i,x,y) ;
    if (y<minVal) minVal=y ;
    if (y>maxVal) maxVal=y ;
  }

  // Second iteration, lower all points by minVal
  for (i=1 ; i<GetN()-1 ; i++) {
    Double_t x,y ;
    GetPoint(i,x,y) ;
    SetPoint(i,x,y-minVal) ;
  }

  // Check if y-axis range needs readjustment
  if (getYAxisMax()>prevYMax) {
    Double_t newMax = maxVal - minVal ;
    setYAxisLimits(getYAxisMin(), newMax<prevYMax ? prevYMax : newMax) ;
  }
}

void RooCurve::addPoints(const RooAbsFunc &func, Double_t xlo, Double_t xhi,
			 Int_t minPoints, Double_t prec, Double_t resolution) {
  // Add points calculated with the specified function, over the range (xlo,xhi).
  // Add at least minPoints equally spaced points, and add sufficient points so that
  // the maximum deviation from the final straight-line segements is prec*(ymax-ymin),
  // down to a minimum horizontal spacing of resolution*(xhi-xlo).

  // check the inputs
  if(!func.isValid()) {
    cout << fName << "::addPoints: input function is not valid" << endl;
    return;
  }
  if(minPoints <= 0 || xhi <= xlo) {
    cout << fName << "::addPoints: bad input (nothing added)" << endl;
    return;
  }

  // Perform a coarse scan of the function to estimate its y range.
  // Save the results so we do not have to re-evaluate at the scan points.
  Double_t *yval= new Double_t[minPoints];
  assert(0 != yval);
  Double_t x,dx= (xhi-xlo)/(minPoints-1.);
  for(Int_t step= 0; step < minPoints; step++) {
    x= xlo + step*dx;
    if (step==minPoints-1) x-=1e-15 ;
    yval[step]= func(&x);
    updateYAxisLimits(yval[step]);
  }

  // store points of the coarse scan and calculate any refinements necessary
  Double_t minDx= resolution*(xhi-xlo);
  Double_t x1,x2= xlo;

  addPoint(xlo-dx,0) ;
  addPoint(xlo-dx,yval[0]) ;

  addPoint(xlo,yval[0]);
  for(Int_t step= 1; step < minPoints; step++) {
    x1= x2;
    x2= xlo + step*dx;
    addRange(func,x1,x2,yval[step-1],yval[step],prec,minDx);
  }
  addPoint(xhi,yval[minPoints-1]) ;

  addPoint(xhi+dx,yval[minPoints-1]) ;
  addPoint(xhi+dx,0) ;

  // cleanup
  delete [] yval;
}

void RooCurve::addRange(const RooAbsFunc& func, Double_t x1, Double_t x2,
			Double_t y1, Double_t y2, Double_t prec, Double_t minDx) {
  // Fill the range (x1,x2) with points calculated using func(&x). No point will
  // be added at x1, and a point will always be added at x2. The density of points
  // will be calculated so that the maximum deviation from a straight line
  // approximation is prec*(ymax-ymin) down to the specified minimum horizontal spacing.

  // calculate our value at the midpoint of this range
  Double_t xmid= 0.5*(x1+x2);
  Double_t ymid= func(&xmid);
  // test if the midpoint is sufficiently close to a straight line across this interval
  Double_t dy= ymid - 0.5*(y1+y2);
  if((xmid - x1 >= minDx) && fabs(dy)>0 && fabs(dy) >= prec*(getYAxisMax()-getYAxisMin())) {
    // fill in each subrange
    updateYAxisLimits(ymid);
    addRange(func,x1,xmid,y1,ymid,prec,minDx);
    addRange(func,xmid,x2,ymid,y2,prec,minDx);
  }
  else {
    // add the endpoint
    addPoint(x2,y2);
  }
}

void RooCurve::addPoint(Double_t x, Double_t y) {
  // Add a point with the specified coordinates. Update our y-axis limits.

  // cout << "RooCurve("<< GetName() << ") adding point at (" << x << "," << y << ")" << endl ;
  Int_t next= GetN();
  SetPoint(next, x, y);
}

Double_t RooCurve::getFitRangeNEvt() const {
  return 1;
}

Double_t RooCurve::getFitRangeBinW() const {
  return 1;
}

void RooCurve::printToStream(ostream& os, PrintOption opt, TString indent) const {
  // Print info about this histogram to the specified output stream.
  //
  //   Standard: number of entries
  //    Verbose: print points on curve

  oneLinePrint(os,*this);
  RooPlotable::printToStream(os,opt,indent);
  if(opt >= Standard) {
    os << indent << "--- RooCurve ---" << endl;
    Int_t n= GetN();
    os << indent << "  Contains " << n << " points" << endl;
    if(opt >= Verbose) {
      os << indent << "  Graph points:" << endl;
      for(Int_t i= 0; i < n; i++) {
	os << indent << setw(3) << i << ") x = " << fX[i] << " , y = " << fY[i] << endl;
      }
    }
  }
}

Double_t RooCurve::chiSquare(const RooHist& hist) const
{
  Int_t i,np = hist.GetN() ;
  Double_t x,y,eyl,eyh ;
  Double_t hbinw2 = hist.getNominalBinWidth()/2 ;

  Double_t chisq(0) ;
  for (i=0 ; i<np ; i++) {
    // Retrieve histogram contents
    ((RooHist&)hist).GetPoint(i,x,y) ;
    eyl = hist.GetEYlow()[i] ;
    eyh = hist.GetEYhigh()[i] ;

    // Integrate function over this bin
    Double_t avg = average(x-hbinw2,x+hbinw2) ;

    // Add pull^2 to chisq
    if (y!=0) {
      Double_t pull = (y>avg) ? ((y-avg)/eyh) : ((y-avg)/eyl) ;
      chisq += pull*pull ;
    }
  }

  // Return chisq/nDOF
  return chisq / np ;
}

Double_t RooCurve::average(Double_t lo, Double_t hi) const
{
  // Find points corresponding to first and last point
  Int_t ifirst = findPoint(lo) ;
  Int_t ilast  = findPoint(hi) ;

  if (ilast<=ifirst) {
    cout << "RooCurve::average(" << GetName()
	 << ") invalid range (" << lo << "," << hi << ")" << endl ;
    return 0 ;
  }

  // Trapezoid integration
  Int_t i ;
  Double_t sum(0),x1,y1,x2,y2 ;
  for (i=ifirst ; i<ilast ; i++) {
    ((RooCurve&)*this).GetPoint(i,x1,y1) ;
    ((RooCurve&)*this).GetPoint(i+1,x2,y2) ;
    sum += (x2-x1)*(y1+y2)/2 ;
  }

  // Return trapezoid sum devided by integration range
  ((RooCurve&)*this).GetPoint(ifirst,x1,y1) ;
  ((RooCurve&)*this).GetPoint(ilast,x2,y2) ;
  return sum/(x2-x1) ;
}

Int_t RooCurve::findPoint(Double_t xvalue) const
{
  Double_t delta(999.),x,y ;
  Int_t i,n = GetN() ;
  Int_t ibest(-1) ;
  for (i=0 ; i<n ; i++) {
    ((RooCurve&)*this).GetPoint(i,x,y) ;
    if (fabs(xvalue-x)<delta) {
      delta = fabs(xvalue-x) ;
      ibest = i ;
    }
  }

  return (delta<1e-10)?ibest:-1 ;
}
<|endoftext|>


<commit_before>//
//  StartUp.cpp
//  mathgame1
//
//  Created by Nicholas on 25/07/2015.
//  Copyright (c) 2015 Nicholas. All rights reserved.
//

#include "StartUp.h"
#include <iostream>
std::string userName;
void setUserName(std::string x)
{
    ::userName = x;
}
std::string getUserName()
{
    return ::userName;
}
void StartUp()
{
    std::cout << "Welcome to the maths game!" <<std::endl;
    std::cout << "Enter your UserName:" << std::endl;
    std::string name;
    std::getline(std::cin, name);
    setUserName(name);
}<commit_msg>simpler design, un necessary factory pattern, works now<commit_after>//
//  StartUp.cpp
//  mathgame1
//
//  Created by Nicholas on 25/07/2015.
//  Copyright (c) 2015 Nicholas. All rights reserved.
//

#include "StartUp.h"
#include <iostream>

std::string StartUp()
{
    std::cout << "Welcome to the maths game!" <<std::endl;
    std::cout << "Enter your UserName:" << std::endl;
    std::string name;
    std::getline(std::cin, name);
    return name;
}<|endoftext|>


<commit_before>#include "rooibos/codegen.hh"

#include "rooibos/InstCodegenVisitor.hh"

using std::set;
using std::string;
using std::vector;

using llvm::Function;

namespace rooibos
{
  void
  codegen(Function & func,
          CodegenContext & ctx,
          vector<StatementAST::ptr> & impls,
          ObjectExpressionAST::ptr asmRet,
          ObjectExpressionAST::ptr adaptors)
  {
      auto & idents = ctx.idents;

      if(func.isDeclaration())
      {
        return;
      }
      auto funcIdent = idents.forFunction(func.getName());
      auto externIdent = idents.forFunctionExtern(func.getName());

      auto impl = FunctionDeclarationAST::create(funcIdent);

      vector<StatementAST::ptr> paramCoercions;
      for(auto & param : func.getArgumentList())
      {
        auto ident = idents.forParameter(param.getName());
        impl->params.push_back(ident);
        auto typeExpr = codegen(idents, &param);
        auto paramType = AssignmentExpressionAST::create(ident, typeExpr);
        paramCoercions.push_back(ExpressionStatementAST::create(paramType));
      }

      vector<VariableDeclaratorAST::ptr> vars;
      vector<StatementAST::ptr> stmts;
      InstCodegenVisitor instVisitor(ctx, vars, stmts);
      instVisitor.visit(func);

      if(ctx.needsStackPointer)
      {
        auto decl = VariableDeclaratorAST::create(ctx.idents.FP,
            NumberLiteralAST::create(0));
        vars.insert(vars.begin(), decl);

        auto coercedFP = BinaryExpressionAST::create(ctx.idents.SP,
            BinaryOp::BITWISE_OR, NumberLiteralAST::create(0));
        auto init = AssignmentExpressionAST::create(ctx.idents.FP,
            coercedFP);
        stmts.insert(stmts.begin(), ExpressionStatementAST::create(init));

        auto fpReset = ExpressionStatementAST::create(
            AssignmentExpressionAST::create(
              ctx.idents.SP,
              BinaryExpressionAST::create(
                ctx.idents.FP,
                BinaryOp::BITWISE_OR,
                NumberLiteralAST::create(0))));
        stmts.insert(stmts.end() - 1, fpReset);
      }

      if(!vars.empty())
      {
        auto decl = VariableDeclarationAST::create();
        decl->decls.insert(decl->decls.begin(), vars.begin(), vars.end());
        stmts.insert(stmts.begin(), decl);
      }

      auto & body = impl->body->body;
      body.insert(body.end(), paramCoercions.begin(), paramCoercions.end());
      body.insert(body.end(), stmts.begin(), stmts.end());

      impls.push_back(impl);
      asmRet->props.push_back(PropertyAST::create(funcIdent, funcIdent));
      adaptors->props.push_back(PropertyAST::create(
            externIdent,
            MemberExpressionAST::create(idents.asm_, funcIdent)));
  }
}
<commit_msg>refactor FP prologue/epilogue<commit_after>#include "rooibos/codegen.hh"

#include "rooibos/InstCodegenVisitor.hh"

using std::set;
using std::string;
using std::vector;

using llvm::Function;

namespace rooibos
{
  namespace
  {
    ExpressionStatementAST::ptr
    codegen_fp_prologue(CodegenContext & ctx)
    {
      auto coercedFP = BinaryExpressionAST::create(ctx.idents.SP,
          BinaryOp::BITWISE_OR, NumberLiteralAST::create(0));
      auto init = AssignmentExpressionAST::create(ctx.idents.FP, coercedFP);
      return ExpressionStatementAST::create(init);
    }

    ExpressionStatementAST::ptr
    codegen_fp_epilogue(CodegenContext & ctx)
    {
      return ExpressionStatementAST::create(
          AssignmentExpressionAST::create(
            ctx.idents.SP,
            BinaryExpressionAST::create(
              ctx.idents.FP,
              BinaryOp::BITWISE_OR,
              NumberLiteralAST::create(0))));
    }
  }

  void
  codegen(Function & func,
          CodegenContext & ctx,
          vector<StatementAST::ptr> & impls,
          ObjectExpressionAST::ptr asmRet,
          ObjectExpressionAST::ptr adaptors)
  {
      auto & idents = ctx.idents;

      if(func.isDeclaration())
      {
        return;
      }
      auto funcIdent = idents.forFunction(func.getName());
      auto externIdent = idents.forFunctionExtern(func.getName());

      auto impl = FunctionDeclarationAST::create(funcIdent);

      vector<StatementAST::ptr> paramCoercions;
      for(auto & param : func.getArgumentList())
      {
        auto ident = idents.forParameter(param.getName());
        impl->params.push_back(ident);
        auto typeExpr = codegen(idents, &param);
        auto paramType = AssignmentExpressionAST::create(ident, typeExpr);
        paramCoercions.push_back(ExpressionStatementAST::create(paramType));
      }

      vector<VariableDeclaratorAST::ptr> vars;
      vector<StatementAST::ptr> stmts;
      InstCodegenVisitor instVisitor(ctx, vars, stmts);
      instVisitor.visit(func);

      if(ctx.needsStackPointer)
      {
        auto decl = VariableDeclaratorAST::create(ctx.idents.FP,
            NumberLiteralAST::create(0));
        vars.insert(vars.begin(), decl);

        stmts.insert(stmts.begin(), codegen_fp_prologue(ctx));
        stmts.insert(stmts.end() - 1, codegen_fp_epilogue(ctx));
      }

      if(!vars.empty())
      {
        auto decl = VariableDeclarationAST::create();
        decl->decls.insert(decl->decls.begin(), vars.begin(), vars.end());
        stmts.insert(stmts.begin(), decl);
      }

      auto & body = impl->body->body;
      body.insert(body.end(), paramCoercions.begin(), paramCoercions.end());
      body.insert(body.end(), stmts.begin(), stmts.end());

      impls.push_back(impl);
      asmRet->props.push_back(PropertyAST::create(funcIdent, funcIdent));
      adaptors->props.push_back(PropertyAST::create(
            externIdent,
            MemberExpressionAST::create(idents.asm_, funcIdent)));
  }
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2008 Digital Bazaar, Inc.  All rights reserved.
 */
#include "db/rt/SharedLock.h"

using namespace db::rt;

SharedLock::SharedLock()
{
   // initialize lock
   pthread_rwlock_init(&mLock, NULL);

   // no locks yet
   mThreadId = 0;
   mLockCount = 0;
}

SharedLock::~SharedLock()
{
}

void SharedLock::lockShared()
{
   // see if this thread holds the exclusive lock
   pthread_t self = pthread_self();
   int rc = pthread_equal(mThreadId, self);
   if(rc == 0)
   {
      // obtain a shared lock
      pthread_rwlock_rdlock(&mLock);
   }
   else
   {
      // current thread has the exclusive lock, so bump up lock count
      mLockCount++;
   }
}

void SharedLock::unlockShared()
{
   // see if this thread holds the exclusive lock
   pthread_t self = pthread_self();
   int rc = pthread_equal(mThreadId, self);
   if(rc == 0)
   {
      // release shared lock
      pthread_rwlock_unlock(&mLock);
   }
   else
   {
      // release exclusive lock
      unlockExclusive();
   }
}

void SharedLock::lockExclusive()
{
   // see if this thread holds the exclusive lock
   pthread_t self = pthread_self();
   int rc = pthread_equal(mThreadId, self);
   if(rc == 0)
   {
      // obtain the exclusive lock
      pthread_rwlock_wrlock(&mLock);

      // set thread that holds the exclusive lock
      mThreadId = self;
   }

   // increment lock count
   mLockCount++;
}

void SharedLock::unlockExclusive()
{
   // decrement lock count
   mLockCount--;

   if(mLockCount == 0)
   {
      // thread no longer holds exclusive lock
      mThreadId = 0;

      // release exclusive lock
      pthread_rwlock_unlock(&mLock);
   }
}
<commit_msg>Added more portable code.<commit_after>/*
 * Copyright (c) 2008 Digital Bazaar, Inc.  All rights reserved.
 */
#include "db/rt/SharedLock.h"

using namespace db::rt;

SharedLock::SharedLock()
{
   // initialize lock
   pthread_rwlock_init(&mLock, NULL);

   // no locks yet
   mThreadId = Thread::getInvalidThreadId();
   mLockCount = 0;
}

SharedLock::~SharedLock()
{
}

void SharedLock::lockShared()
{
   // see if this thread holds the exclusive lock
   pthread_t self = pthread_self();
   int rc = pthread_equal(mThreadId, self);
   if(rc == 0)
   {
      // obtain a shared lock
      pthread_rwlock_rdlock(&mLock);
   }
   else
   {
      // current thread has the exclusive lock, so bump up lock count
      mLockCount++;
   }
}

void SharedLock::unlockShared()
{
   // see if this thread holds the exclusive lock
   pthread_t self = pthread_self();
   int rc = pthread_equal(mThreadId, self);
   if(rc == 0)
   {
      // release shared lock
      pthread_rwlock_unlock(&mLock);
   }
   else
   {
      // release exclusive lock
      unlockExclusive();
   }
}

void SharedLock::lockExclusive()
{
   // see if this thread holds the exclusive lock
   pthread_t self = pthread_self();
   int rc = pthread_equal(mThreadId, self);
   if(rc == 0)
   {
      // obtain the exclusive lock
      pthread_rwlock_wrlock(&mLock);

      // set thread that holds the exclusive lock
      mThreadId = self;
   }

   // increment lock count
   mLockCount++;
}

void SharedLock::unlockExclusive()
{
   // decrement lock count
   mLockCount--;

   if(mLockCount == 0)
   {
      // thread no longer holds exclusive lock
      mThreadId = Thread::getInvalidThreadId();

      // release exclusive lock
      pthread_rwlock_unlock(&mLock);
   }
}
<|endoftext|>


<commit_before>#include "GRenderContext.h"
#include <fstream>
#include <cmath>
#include "GFrameBufferObject.h"
#include "GLUtil.h"
#include "Util.h"
#include <EGL/egl.h>
#include <vector>

namespace NodeBinding
{

    static std::vector<GRenderContext *> g_RenderContextVC;
    static EGLContext g_eglContext = EGL_NO_CONTEXT;
    static EGLDisplay g_eglDisplay = EGL_NO_DISPLAY;

    GRenderContext::GRenderContext(int width, int height)
        : mWidth(width), mHeight(height), mRatio(2.0), mEglDisplay(EGL_NO_DISPLAY)
    {
        GCanvasConfig config = {true, false};
        this->mCanvas = std::make_shared<gcanvas::GCanvas>("node-gcanvas", config, nullptr);
        mCanvasWidth = width * mRatio;
        mCanvasHeight = height * mRatio;
    }

    GRenderContext::GRenderContext(int width, int height, int ratio)
        : mWidth(width), mHeight(height), mRatio(ratio), mEglDisplay(EGL_NO_DISPLAY)
    {
        GCanvasConfig config = {true, true};
        this->mCanvas = std::make_shared<gcanvas::GCanvas>("node-gcanvas", config, nullptr);
        mCanvasWidth = width * mRatio;
        mCanvasHeight = height * mRatio;
    }

    void GRenderContext::initRenderEnviroment()
    {
        InitSharedContextIfNot();
#ifdef CONTEXT_ES20
        EGLint ai32ContextAttribs[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
#endif

        // Step 1 - Get the default display.
        if (!mEglDisplay)
        {
            mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
            // Step 2 - Initialize EGL.
            eglInitialize(mEglDisplay, 0, 0);
        }

#ifdef CONTEXT_ES20
        // Step 3 - Make OpenGL ES the current API.
        eglBindAPI(EGL_OPENGL_ES_API);

        // Step 4 - Specify the required configuration attributes.
        EGLint pi32ConfigAttribs[5];
        pi32ConfigAttribs[0] = EGL_SURFACE_TYPE;
        pi32ConfigAttribs[1] = EGL_WINDOW_BIT;
        pi32ConfigAttribs[2] = EGL_RENDERABLE_TYPE;
        pi32ConfigAttribs[3] = EGL_OPENGL_ES2_BIT;
        pi32ConfigAttribs[4] = EGL_NONE;
#else
        EGLint pi32ConfigAttribs[3];
        pi32ConfigAttribs[0] = EGL_SURFACE_TYPE;
        pi32ConfigAttribs[1] = EGL_WINDOW_BIT;
        pi32ConfigAttribs[2] = EGL_NONE;
#endif

        // Step 5 - Find a config that matches all requirements.
        int iConfigs;
        EGLConfig eglConfig;
        eglChooseConfig(mEglDisplay, pi32ConfigAttribs, &eglConfig, 1,
                        &iConfigs);
        if (iConfigs != 1)
        {
            printf("Error: eglChooseConfig(): config not found \n");
            exit(-1);
        }

        // Step 6 - Create a surface to draw to.

        mEglSurface = eglCreatePbufferSurface(mEglDisplay, eglConfig, NULL);
        // Step 7 - Create a context.

        if (g_eglContext == EGL_NO_CONTEXT)
        {
#ifdef CONTEXT_ES20
            mEglContext = eglCreateContext(mEglDisplay, eglConfig, NULL, ai32ContextAttribs);
#else
            mEglContext = eglCreateContext(mEglDisplay, eglConfig, NULL, NULL);
#endif
            // g_eglContext = mEglContext;
        }
        else
        {
#ifdef CONTEXT_ES20
            mEglContext = eglCreateContext(mEglDisplay, eglConfig, g_eglContext, ai32ContextAttribs);
#else
            mEglContext = eglCreateContext(mEglDisplay, eglConfig, NULL, NULL);
#endif
        }

        // Step 8 - Bind the context to the current thread
        if (eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext) != EGL_TRUE)
        {
            EGLint error = eglGetError();
            printf("eglMakeCurrent fail the error is %x\n", error);
            exit(-1);
        }
        // end of standard gl context setup

        // Step 9 - create framebuffer object
        glGenFramebuffers(1, &mFboId);
        glBindFramebuffer(GL_FRAMEBUFFER, mFboId);

        glGenRenderbuffers(1, &mRenderBuffer);
        glBindRenderbuffer(GL_RENDERBUFFER, mRenderBuffer);
        glRenderbufferStorage(GL_RENDERBUFFER, GL_RGB565, mCanvasWidth, mCanvasHeight);

        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, mRenderBuffer);
        glGenRenderbuffers(1, &mDepthRenderbuffer);
        glBindRenderbuffer(GL_RENDERBUFFER, mDepthRenderbuffer);
        glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8_OES, mCanvasWidth, mCanvasHeight);
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, mDepthRenderbuffer);
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, mDepthRenderbuffer);
        // check FBO status
        GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
        if (status != GL_FRAMEBUFFER_COMPLETE)
        {
            EGLint error = eglGetError();
            printf("Problem with OpenGL framebuffer after specifying color render buffer:  %x  the glError is %x \n", status, error);
            exit(-1);
        }
        else
        {
            printf("FBO create success %p, fboId=%d, renderbufferId=%d depthbufferId=%d\n", this, mFboId, mRenderBuffer, mDepthRenderbuffer);
        }

        GLint format = 0, type = 0;
        glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_FORMAT, &format);
        glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_TYPE, &type);
        this->initCanvas();
        g_RenderContextVC.push_back(this);
    }

    void GRenderContext::makeCurrent()
    {
        if (mEglContext != nullptr)
        {
            //判断当前上下文是否是该canvas的上下文,减少makecurrent的切换过程
            EGLContext currentContext = eglGetCurrentContext();
            EGLSurface currentSurface = eglGetCurrentSurface(EGL_DRAW);
            if (mEglContext == currentContext && mEglSurface == currentSurface)
            {
                return;
            }
        }
        if (mEglContext != EGL_NO_CONTEXT && mEglDisplay != EGL_NO_DISPLAY)
        {
            if (eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext) != EGL_TRUE)
            {
                printf("eglMakeCurrent fail \n");
                exit(-1);
            }
        }
        this->BindFBO();
    }

    void GRenderContext::initCanvas()
    {
        mCanvas->CreateContext();
        mCanvas->GetGCanvasContext()->SetClearColor(gcanvas::StrValueToColorRGBA("white"));
        mCanvas->GetGCanvasContext()->ClearScreen();
        mCanvas->GetGCanvasContext()->SetDevicePixelRatio(mRatio);
        mCanvas->OnSurfaceChanged(0, 0, mCanvasWidth, mCanvasHeight);
    }
    void GRenderContext::drawFrame()
    {
        mCanvas->drawFrame();
        this->drawCount++;
    }

    int GRenderContext::getImagePixelPNG(std::vector<unsigned char> &in)
    {
        unsigned char *data = new unsigned char[4 * mWidth * mHeight];
        int ret = this->readPixelAndSampleFromCurrentCtx(data);
        if (ret == 0)
        {
            encodePNGInBuffer(in, data, mWidth, mHeight);
        }
        else
        {
            delete data;
            data = nullptr;
            return -1;
        }
        delete data;
        data = nullptr;
        return 0;
    }

    int GRenderContext::getImagePixelJPG(unsigned char **in, unsigned long &size)
    {
        unsigned char *data = new unsigned char[4 * mWidth * mHeight];
        int ret = this->readPixelAndSampleFromCurrentCtx(data);
        if (ret == 0)
        {
            encodeJPEGInBuffer(in, size, data, mWidth, mHeight);
        }
        else
        {
            delete data;
            data = nullptr;
            return -1;
        }
        delete data;
        data = nullptr;
        return 0;
    }

    int GRenderContext::readPixelAndSampleFromCurrentCtx(unsigned char *data)
    {
        //canvas的width默认是width的2倍,这样可以增高分辨率
        int size = 4 * mCanvasWidth * mCanvasHeight;
        unsigned char *canvasData = new unsigned char[size];
        if (!canvasData)
        {
            printf("Error: allocate inputData memeroy faied! \n");
            return -1;
        }
        glReadPixels(0, 0, mCanvasWidth, mCanvasHeight, GL_RGBA, GL_UNSIGNED_BYTE, canvasData);

        if (!data)
        {
            printf("Error: allocate data memeroy faied! \n");
            delete canvasData;
            canvasData = nullptr;
            return -1;
        }
        //使用gcanvas进行采样，提高导出图片的清晰度
        gcanvas::PixelsSampler(mCanvasWidth, mCanvasHeight, (int *)canvasData, mWidth, mHeight, (int *)data);
        gcanvas::FlipPixel(data, mWidth, mHeight);
        delete canvasData;
        canvasData = nullptr;
        return 0;
    }
    void GRenderContext::render2file(std::string fileName, PIC_FORMAT format)
    {
        unsigned char *data = new unsigned char[4 * mWidth * mHeight];
        int ret = this->readPixelAndSampleFromCurrentCtx(data);
        if (ret == 0)
        {
            if (format == PNG_FORAMT)
            {
                encodePixelsToPNGFile(fileName + ".png", data, mWidth, mHeight);
            }
            else if (format == JPEG_FORMAT)
            {
                encodePixelsToJPEGFile(fileName + ".jpg", data, mWidth, mHeight);
            }
        }
        else
        //todo
        {
        }
        delete data;
        data = nullptr;
    }

    void GRenderContext::destoryRenderEnviroment()
    {
        if (this->mFboId != 0)
        {
            glDeleteFramebuffers(1, &this->mFboId);
        }
        if (this->mRenderBuffer != 0)
        {
            glDeleteRenderbuffers(1, &this->mRenderBuffer);
        }
        if (this->mDepthRenderbuffer != 0)
        {
            glDeleteRenderbuffers(1, &this->mDepthRenderbuffer);
        }
        if (this->textures.size() > 0)
        {
            glDeleteTextures(this->textures.size(), (GLuint *)&textures[0]);
        }

        if (mEglSurface != EGL_NO_SURFACE)
        {
            eglDestroySurface(mEglDisplay, mEglSurface);
            mEglSurface = EGL_NO_SURFACE;
        }
        std::vector<GRenderContext *>::iterator iter = find(g_RenderContextVC.begin(), g_RenderContextVC.end(), this);
        if (iter != g_RenderContextVC.end())
        {
            g_RenderContextVC.erase(iter);
        }
        if (mEglDisplay != EGL_NO_DISPLAY)
        {
            eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
            if (mEglContext != EGL_NO_CONTEXT)
            {
                eglDestroyContext(mEglDisplay, mEglContext);
            }
            // eglTerminate(mEglDisplay);
        }
        mEglDisplay = EGL_NO_DISPLAY;
        mEglContext = EGL_NO_CONTEXT;
        //todo 考虑何时释放sharedContext?不用释放？等待进程销毁
        // if (g_RenderContextVC.size() == 0)
        // {
        //     g_eglContext = EGL_NO_CONTEXT;
        // }
    }

    void GRenderContext::recordTextures(int textureId)
    {
        this->textures.push_back(textureId);
    }

    void GRenderContext::BindFBO()
    {
        GLint curFBOId = 0;
        glGetIntegerv(GL_FRAMEBUFFER_BINDING, &curFBOId);
        if (curFBOId != mFboId)
        {
            glBindFramebuffer(GL_FRAMEBUFFER, mFboId);
            // printf("bindfbo value is %d\n", mFboId);
        }
    }

    void GRenderContext::InitSharedContextIfNot()
    {
        if (g_eglDisplay == EGL_NO_DISPLAY && g_eglContext == EGL_NO_CONTEXT)
        {
#ifdef CONTEXT_ES20
            EGLint ai32ContextAttribs[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
#endif
            g_eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
            // Step 2 - Initialize EGL.
            eglInitialize(g_eglDisplay, 0, 0);

            // Step 3 - Make OpenGL ES the current API.
            eglBindAPI(EGL_OPENGL_ES_API);

            // Step 4 - Specify the required configuration attributes.
            EGLint pi32ConfigAttribs[5];
            pi32ConfigAttribs[0] = EGL_SURFACE_TYPE;
            pi32ConfigAttribs[1] = EGL_WINDOW_BIT;
            pi32ConfigAttribs[2] = EGL_RENDERABLE_TYPE;
            pi32ConfigAttribs[3] = EGL_OPENGL_ES2_BIT;
            pi32ConfigAttribs[4] = EGL_NONE;

            // Step 5 - Find a config that matches all requirements.
            int iConfigs;
            EGLConfig eglConfig;
            eglChooseConfig(g_eglDisplay, pi32ConfigAttribs, &eglConfig, 1,
                            &iConfigs);
            if (iConfigs != 1)
            {
                printf("Error: eglChooseConfig(): config not found \n");
                exit(-1);
            }

            // Step 7 - Create a context.

            if (g_eglContext == EGL_NO_CONTEXT)
            {
#ifdef CONTEXT_ES20
                g_eglContext = eglCreateContext(g_eglDisplay, eglConfig, NULL, ai32ContextAttribs);
#else
                g_eglContext = eglCreateContext(mEglDisplay, eglConfig, NULL, NULL);
#endif
            }
        }
    }

    GRenderContext::~GRenderContext()
    {
        this->destoryRenderEnviroment();
    }
} // namespace NodeBinding
<commit_msg>refactor: change the display init jugdement<commit_after>#include "GRenderContext.h"
#include <fstream>
#include <cmath>
#include "GFrameBufferObject.h"
#include "GLUtil.h"
#include "Util.h"
#include <EGL/egl.h>
#include <vector>

namespace NodeBinding
{

    static std::vector<GRenderContext *> g_RenderContextVC;
    static EGLContext g_eglContext = EGL_NO_CONTEXT;
    static EGLDisplay g_eglDisplay = EGL_NO_DISPLAY;

    GRenderContext::GRenderContext(int width, int height)
        : mWidth(width), mHeight(height), mRatio(2.0), mEglDisplay(EGL_NO_DISPLAY)
    {
        GCanvasConfig config = {true, false};
        this->mCanvas = std::make_shared<gcanvas::GCanvas>("node-gcanvas", config, nullptr);
        mCanvasWidth = width * mRatio;
        mCanvasHeight = height * mRatio;
    }

    GRenderContext::GRenderContext(int width, int height, int ratio)
        : mWidth(width), mHeight(height), mRatio(ratio), mEglDisplay(EGL_NO_DISPLAY)
    {
        GCanvasConfig config = {true, true};
        this->mCanvas = std::make_shared<gcanvas::GCanvas>("node-gcanvas", config, nullptr);
        mCanvasWidth = width * mRatio;
        mCanvasHeight = height * mRatio;
    }

    void GRenderContext::initRenderEnviroment()
    {
        InitSharedContextIfNot();
#ifdef CONTEXT_ES20
        EGLint ai32ContextAttribs[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
#endif
        // Step 1 - Get the default display.
        if (mEglDisplay == EGL_NO_DISPLAY)
        {
            mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
            // Step 2 - Initialize EGL.
            eglInitialize(mEglDisplay, 0, 0);
        }

#ifdef CONTEXT_ES20
        // Step 3 - Make OpenGL ES the current API.
        eglBindAPI(EGL_OPENGL_ES_API);

        // Step 4 - Specify the required configuration attributes.
        EGLint pi32ConfigAttribs[5];
        pi32ConfigAttribs[0] = EGL_SURFACE_TYPE;
        pi32ConfigAttribs[1] = EGL_WINDOW_BIT;
        pi32ConfigAttribs[2] = EGL_RENDERABLE_TYPE;
        pi32ConfigAttribs[3] = EGL_OPENGL_ES2_BIT;
        pi32ConfigAttribs[4] = EGL_NONE;
#else
        EGLint pi32ConfigAttribs[3];
        pi32ConfigAttribs[0] = EGL_SURFACE_TYPE;
        pi32ConfigAttribs[1] = EGL_WINDOW_BIT;
        pi32ConfigAttribs[2] = EGL_NONE;
#endif

        // Step 5 - Find a config that matches all requirements.
        int iConfigs;
        EGLConfig eglConfig;
        eglChooseConfig(mEglDisplay, pi32ConfigAttribs, &eglConfig, 1,
                        &iConfigs);
        if (iConfigs != 1)
        {
            printf("Error: eglChooseConfig(): config not found \n");
            exit(-1);
        }

        // Step 6 - Create a surface to draw to.

        mEglSurface = eglCreatePbufferSurface(mEglDisplay, eglConfig, NULL);
        // Step 7 - Create a context.

        if (g_eglContext == EGL_NO_CONTEXT)
        {
#ifdef CONTEXT_ES20
            mEglContext = eglCreateContext(mEglDisplay, eglConfig, NULL, ai32ContextAttribs);
#else
            mEglContext = eglCreateContext(mEglDisplay, eglConfig, NULL, NULL);
#endif
            // g_eglContext = mEglContext;
        }
        else
        {
#ifdef CONTEXT_ES20
            mEglContext = eglCreateContext(mEglDisplay, eglConfig, g_eglContext, ai32ContextAttribs);
#else
            mEglContext = eglCreateContext(mEglDisplay, eglConfig, NULL, NULL);
#endif
        }

        // Step 8 - Bind the context to the current thread
        if (eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext) != EGL_TRUE)
        {
            EGLint error = eglGetError();
            printf("eglMakeCurrent fail the error is %x\n", error);
            exit(-1);
        }
        // end of standard gl context setup

        // Step 9 - create framebuffer object
        glGenFramebuffers(1, &mFboId);
        glBindFramebuffer(GL_FRAMEBUFFER, mFboId);

        glGenRenderbuffers(1, &mRenderBuffer);
        glBindRenderbuffer(GL_RENDERBUFFER, mRenderBuffer);
        glRenderbufferStorage(GL_RENDERBUFFER, GL_RGB565, mCanvasWidth, mCanvasHeight);

        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, mRenderBuffer);
        glGenRenderbuffers(1, &mDepthRenderbuffer);
        glBindRenderbuffer(GL_RENDERBUFFER, mDepthRenderbuffer);
        glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8_OES, mCanvasWidth, mCanvasHeight);
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, mDepthRenderbuffer);
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, mDepthRenderbuffer);
        // check FBO status
        GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
        if (status != GL_FRAMEBUFFER_COMPLETE)
        {
            EGLint error = eglGetError();
            printf("Problem with OpenGL framebuffer after specifying color render buffer:  %x  the glError is %x \n", status, error);
            exit(-1);
        }
        else
        {
            printf("FBO create success %p, fboId=%d, renderbufferId=%d depthbufferId=%d\n", this, mFboId, mRenderBuffer, mDepthRenderbuffer);
        }

        GLint format = 0, type = 0;
        glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_FORMAT, &format);
        glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_TYPE, &type);
        this->initCanvas();
        g_RenderContextVC.push_back(this);
    }

    void GRenderContext::makeCurrent()
    {
        if (mEglContext != nullptr)
        {
            //判断当前上下文是否是该canvas的上下文,减少makecurrent的切换过程
            EGLContext currentContext = eglGetCurrentContext();
            EGLSurface currentSurface = eglGetCurrentSurface(EGL_DRAW);
            if (mEglContext == currentContext && mEglSurface == currentSurface)
            {
                return;
            }
        }
        if (mEglContext != EGL_NO_CONTEXT && mEglDisplay != EGL_NO_DISPLAY)
        {
            if (eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext) != EGL_TRUE)
            {
                printf("eglMakeCurrent fail \n");
                exit(-1);
            }
        }
        this->BindFBO();
    }

    void GRenderContext::initCanvas()
    {
        mCanvas->CreateContext();
        mCanvas->GetGCanvasContext()->SetClearColor(gcanvas::StrValueToColorRGBA("white"));
        mCanvas->GetGCanvasContext()->ClearScreen();
        mCanvas->GetGCanvasContext()->SetDevicePixelRatio(mRatio);
        mCanvas->OnSurfaceChanged(0, 0, mCanvasWidth, mCanvasHeight);
    }
    void GRenderContext::drawFrame()
    {
        mCanvas->drawFrame();
        this->drawCount++;
    }

    int GRenderContext::getImagePixelPNG(std::vector<unsigned char> &in)
    {
        unsigned char *data = new unsigned char[4 * mWidth * mHeight];
        int ret = this->readPixelAndSampleFromCurrentCtx(data);
        if (ret == 0)
        {
            encodePNGInBuffer(in, data, mWidth, mHeight);
        }
        else
        {
            delete data;
            data = nullptr;
            return -1;
        }
        delete data;
        data = nullptr;
        return 0;
    }

    int GRenderContext::getImagePixelJPG(unsigned char **in, unsigned long &size)
    {
        unsigned char *data = new unsigned char[4 * mWidth * mHeight];
        int ret = this->readPixelAndSampleFromCurrentCtx(data);
        if (ret == 0)
        {
            encodeJPEGInBuffer(in, size, data, mWidth, mHeight);
        }
        else
        {
            delete data;
            data = nullptr;
            return -1;
        }
        delete data;
        data = nullptr;
        return 0;
    }

    int GRenderContext::readPixelAndSampleFromCurrentCtx(unsigned char *data)
    {
        //canvas的width默认是width的2倍,这样可以增高分辨率
        int size = 4 * mCanvasWidth * mCanvasHeight;
        unsigned char *canvasData = new unsigned char[size];
        if (!canvasData)
        {
            printf("Error: allocate inputData memeroy faied! \n");
            return -1;
        }
        glReadPixels(0, 0, mCanvasWidth, mCanvasHeight, GL_RGBA, GL_UNSIGNED_BYTE, canvasData);

        if (!data)
        {
            printf("Error: allocate data memeroy faied! \n");
            delete canvasData;
            canvasData = nullptr;
            return -1;
        }
        //使用gcanvas进行采样，提高导出图片的清晰度
        gcanvas::PixelsSampler(mCanvasWidth, mCanvasHeight, (int *)canvasData, mWidth, mHeight, (int *)data);
        gcanvas::FlipPixel(data, mWidth, mHeight);
        delete canvasData;
        canvasData = nullptr;
        return 0;
    }
    void GRenderContext::render2file(std::string fileName, PIC_FORMAT format)
    {
        unsigned char *data = new unsigned char[4 * mWidth * mHeight];
        int ret = this->readPixelAndSampleFromCurrentCtx(data);
        if (ret == 0)
        {
            if (format == PNG_FORAMT)
            {
                encodePixelsToPNGFile(fileName + ".png", data, mWidth, mHeight);
            }
            else if (format == JPEG_FORMAT)
            {
                encodePixelsToJPEGFile(fileName + ".jpg", data, mWidth, mHeight);
            }
        }
        else
        //todo
        {
        }
        delete data;
        data = nullptr;
    }

    void GRenderContext::destoryRenderEnviroment()
    {
        if (this->mFboId != 0)
        {
            glDeleteFramebuffers(1, &this->mFboId);
        }
        if (this->mRenderBuffer != 0)
        {
            glDeleteRenderbuffers(1, &this->mRenderBuffer);
        }
        if (this->mDepthRenderbuffer != 0)
        {
            glDeleteRenderbuffers(1, &this->mDepthRenderbuffer);
        }
        if (this->textures.size() > 0)
        {
            glDeleteTextures(this->textures.size(), (GLuint *)&textures[0]);
        }

        if (mEglSurface != EGL_NO_SURFACE)
        {
            eglDestroySurface(mEglDisplay, mEglSurface);
            mEglSurface = EGL_NO_SURFACE;
        }
        std::vector<GRenderContext *>::iterator iter = find(g_RenderContextVC.begin(), g_RenderContextVC.end(), this);
        if (iter != g_RenderContextVC.end())
        {
            g_RenderContextVC.erase(iter);
        }
        if (mEglDisplay != EGL_NO_DISPLAY)
        {
            eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
            if (mEglContext != EGL_NO_CONTEXT)
            {
                eglDestroyContext(mEglDisplay, mEglContext);
            }
        }
        mEglDisplay = EGL_NO_DISPLAY;
        mEglContext = EGL_NO_CONTEXT;
        //todo 考虑何时释放sharedContext?不用释放？等待进程销毁
        // if (g_RenderContextVC.size() == 0)
        // {
        //     g_eglContext = EGL_NO_CONTEXT;
        // }
    }

    void GRenderContext::recordTextures(int textureId)
    {
        this->textures.push_back(textureId);
    }

    void GRenderContext::BindFBO()
    {
        GLint curFBOId = 0;
        glGetIntegerv(GL_FRAMEBUFFER_BINDING, &curFBOId);
        if (curFBOId != mFboId)
        {
            glBindFramebuffer(GL_FRAMEBUFFER, mFboId);
            // printf("bindfbo value is %d\n", mFboId);
        }
    }

    void GRenderContext::InitSharedContextIfNot()
    {
        if (g_eglDisplay == EGL_NO_DISPLAY && g_eglContext == EGL_NO_CONTEXT)
        {
#ifdef CONTEXT_ES20
            EGLint ai32ContextAttribs[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
#endif
            g_eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
            // Step 2 - Initialize EGL.
            eglInitialize(g_eglDisplay, 0, 0);

            // Step 3 - Make OpenGL ES the current API.
            eglBindAPI(EGL_OPENGL_ES_API);

            // Step 4 - Specify the required configuration attributes.
            EGLint pi32ConfigAttribs[5];
            pi32ConfigAttribs[0] = EGL_SURFACE_TYPE;
            pi32ConfigAttribs[1] = EGL_WINDOW_BIT;
            pi32ConfigAttribs[2] = EGL_RENDERABLE_TYPE;
            pi32ConfigAttribs[3] = EGL_OPENGL_ES2_BIT;
            pi32ConfigAttribs[4] = EGL_NONE;

            // Step 5 - Find a config that matches all requirements.
            int iConfigs;
            EGLConfig eglConfig;
            eglChooseConfig(g_eglDisplay, pi32ConfigAttribs, &eglConfig, 1,
                            &iConfigs);
            if (iConfigs != 1)
            {
                printf("Error: eglChooseConfig(): config not found \n");
                exit(-1);
            }

            // Step 7 - Create a context.

            if (g_eglContext == EGL_NO_CONTEXT)
            {
#ifdef CONTEXT_ES20
                g_eglContext = eglCreateContext(g_eglDisplay, eglConfig, NULL, ai32ContextAttribs);
#else
                g_eglContext = eglCreateContext(mEglDisplay, eglConfig, NULL, NULL);
#endif
            }
        }
    }

    GRenderContext::~GRenderContext()
    {
        this->destoryRenderEnviroment();
    }
} // namespace NodeBinding
<|endoftext|>


<commit_before>//===- LowerExpectIntrinsic.cpp - Lower expect intrinsic ------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This pass lowers the 'expect' intrinsic to LLVM metadata.
//
//===----------------------------------------------------------------------===//

#include "llvm/Transforms/Scalar/LowerExpectIntrinsic.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/Intrinsics.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/MDBuilder.h"
#include "llvm/IR/Metadata.h"
#include "llvm/Pass.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
#include "llvm/Transforms/Scalar.h"

using namespace llvm;

#define DEBUG_TYPE "lower-expect-intrinsic"

STATISTIC(ExpectIntrinsicsHandled,
          "Number of 'expect' intrinsic instructions handled");

// These default values are chosen to represent an extremely skewed outcome for
// a condition, but they leave some room for interpretation by later passes.
//
// If the documentation for __builtin_expect() was made explicit that it should
// only be used in extreme cases, we could make this ratio higher. As it stands,
// programmers may be using __builtin_expect() / llvm.expect to annotate that a
// branch is likely or unlikely to be taken.
//
// There is a known dependency on this ratio in CodeGenPrepare when transforming
// 'select' instructions. It may be worthwhile to hoist these values to some
// shared space, so they can be used directly by other passes.

static cl::opt<uint32_t> LikelyBranchWeight(
    "likely-branch-weight", cl::Hidden, cl::init(2000),
    cl::desc("Weight of the branch likely to be taken (default = 2000)"));
static cl::opt<uint32_t> UnlikelyBranchWeight(
    "unlikely-branch-weight", cl::Hidden, cl::init(1),
    cl::desc("Weight of the branch unlikely to be taken (default = 1)"));

static bool handleSwitchExpect(SwitchInst &SI) {
  CallInst *CI = dyn_cast<CallInst>(SI.getCondition());
  if (!CI)
    return false;

  Function *Fn = CI->getCalledFunction();
  if (!Fn || Fn->getIntrinsicID() != Intrinsic::expect)
    return false;

  Value *ArgValue = CI->getArgOperand(0);
  ConstantInt *ExpectedValue = dyn_cast<ConstantInt>(CI->getArgOperand(1));
  if (!ExpectedValue)
    return false;

  SwitchInst::CaseHandle Case = *SI.findCaseValue(ExpectedValue);
  unsigned n = SI.getNumCases(); // +1 for default case.
  SmallVector<uint32_t, 16> Weights(n + 1, UnlikelyBranchWeight);

  if (Case == *SI.case_default())
    Weights[0] = LikelyBranchWeight;
  else
    Weights[Case.getCaseIndex() + 1] = LikelyBranchWeight;

  SI.setMetadata(LLVMContext::MD_prof,
                 MDBuilder(CI->getContext()).createBranchWeights(Weights));

  SI.setCondition(ArgValue);
  return true;
}

// Handle both BranchInst and SelectInst.
template <class BrSelInst> static bool handleBrSelExpect(BrSelInst &BSI) {

  // Handle non-optimized IR code like:
  //   %expval = call i64 @llvm.expect.i64(i64 %conv1, i64 1)
  //   %tobool = icmp ne i64 %expval, 0
  //   br i1 %tobool, label %if.then, label %if.end
  //
  // Or the following simpler case:
  //   %expval = call i1 @llvm.expect.i1(i1 %cmp, i1 1)
  //   br i1 %expval, label %if.then, label %if.end

  CallInst *CI;

  ICmpInst *CmpI = dyn_cast<ICmpInst>(BSI.getCondition());
  CmpInst::Predicate Predicate;
  uint64_t ValueComparedTo = 0;
  if (!CmpI) {
    CI = dyn_cast<CallInst>(BSI.getCondition());
    Predicate = CmpInst::ICMP_NE;
    ValueComparedTo = 0;
  } else {
    Predicate = CmpI->getPredicate();
    if (Predicate != CmpInst::ICMP_NE && Predicate != CmpInst::ICMP_EQ)
      return false;
    ConstantInt *CmpConstOperand = dyn_cast<ConstantInt>(CmpI->getOperand(1));
    if (!CmpConstOperand)
      return false;
    ValueComparedTo = CmpConstOperand->getZExtValue();
    CI = dyn_cast<CallInst>(CmpI->getOperand(0));
  }

  if (!CI)
    return false;

  Function *Fn = CI->getCalledFunction();
  if (!Fn || Fn->getIntrinsicID() != Intrinsic::expect)
    return false;

  Value *ArgValue = CI->getArgOperand(0);
  ConstantInt *ExpectedValue = dyn_cast<ConstantInt>(CI->getArgOperand(1));
  if (!ExpectedValue)
    return false;

  MDBuilder MDB(CI->getContext());
  MDNode *Node;

  if ((ExpectedValue->getZExtValue() == ValueComparedTo) ==
      (Predicate == CmpInst::ICMP_EQ))
    Node = MDB.createBranchWeights(LikelyBranchWeight, UnlikelyBranchWeight);
  else
    Node = MDB.createBranchWeights(UnlikelyBranchWeight, LikelyBranchWeight);

  BSI.setMetadata(LLVMContext::MD_prof, Node);

  if (CmpI)
    CmpI->setOperand(0, ArgValue);
  else
    BSI.setCondition(ArgValue);
  return true;
}

static bool handleBranchExpect(BranchInst &BI) {
  if (BI.isUnconditional())
    return false;

  return handleBrSelExpect<BranchInst>(BI);
}

static bool lowerExpectIntrinsic(Function &F) {
  bool Changed = false;

  for (BasicBlock &BB : F) {
    // Create "block_weights" metadata.
    if (BranchInst *BI = dyn_cast<BranchInst>(BB.getTerminator())) {
      if (handleBranchExpect(*BI))
        ExpectIntrinsicsHandled++;
    } else if (SwitchInst *SI = dyn_cast<SwitchInst>(BB.getTerminator())) {
      if (handleSwitchExpect(*SI))
        ExpectIntrinsicsHandled++;
    }

    // Remove llvm.expect intrinsics. Iterate backwards in order
    // to process select instructions before the intrinsic gets
    // removed.
    for (auto BI = BB.rbegin(), BE = BB.rend(); BI != BE;) {
      Instruction *Inst = &*BI++;
      CallInst *CI = dyn_cast<CallInst>(Inst);
      if (!CI) {
        if (SelectInst *SI = dyn_cast<SelectInst>(Inst)) {
          if (handleBrSelExpect(*SI))
            ExpectIntrinsicsHandled++;
        }
        continue;
      }

      Function *Fn = CI->getCalledFunction();
      if (Fn && Fn->getIntrinsicID() == Intrinsic::expect) {
        Value *Exp = CI->getArgOperand(0);
        CI->replaceAllUsesWith(Exp);
        CI->eraseFromParent();
        Changed = true;
      }
    }
  }

  return Changed;
}

PreservedAnalyses LowerExpectIntrinsicPass::run(Function &F,
                                                FunctionAnalysisManager &) {
  if (lowerExpectIntrinsic(F))
    return PreservedAnalyses::none();

  return PreservedAnalyses::all();
}

namespace {
/// \brief Legacy pass for lowering expect intrinsics out of the IR.
///
/// When this pass is run over a function it uses expect intrinsics which feed
/// branches and switches to provide branch weight metadata for those
/// terminators. It then removes the expect intrinsics from the IR so the rest
/// of the optimizer can ignore them.
class LowerExpectIntrinsic : public FunctionPass {
public:
  static char ID;
  LowerExpectIntrinsic() : FunctionPass(ID) {
    initializeLowerExpectIntrinsicPass(*PassRegistry::getPassRegistry());
  }

  bool runOnFunction(Function &F) override { return lowerExpectIntrinsic(F); }
};
}

char LowerExpectIntrinsic::ID = 0;
INITIALIZE_PASS(LowerExpectIntrinsic, "lower-expect",
                "Lower 'expect' Intrinsics", false, false)

FunctionPass *llvm::createLowerExpectIntrinsicPass() {
  return new LowerExpectIntrinsic();
}
<commit_msg>Fix compiler_rt buildbot failure<commit_after>//===- LowerExpectIntrinsic.cpp - Lower expect intrinsic ------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This pass lowers the 'expect' intrinsic to LLVM metadata.
//
//===----------------------------------------------------------------------===//

#include "llvm/Transforms/Scalar/LowerExpectIntrinsic.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/Intrinsics.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/MDBuilder.h"
#include "llvm/IR/Metadata.h"
#include "llvm/Pass.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
#include "llvm/Transforms/Scalar.h"

using namespace llvm;

#define DEBUG_TYPE "lower-expect-intrinsic"

STATISTIC(ExpectIntrinsicsHandled,
          "Number of 'expect' intrinsic instructions handled");

// These default values are chosen to represent an extremely skewed outcome for
// a condition, but they leave some room for interpretation by later passes.
//
// If the documentation for __builtin_expect() was made explicit that it should
// only be used in extreme cases, we could make this ratio higher. As it stands,
// programmers may be using __builtin_expect() / llvm.expect to annotate that a
// branch is likely or unlikely to be taken.
//
// There is a known dependency on this ratio in CodeGenPrepare when transforming
// 'select' instructions. It may be worthwhile to hoist these values to some
// shared space, so they can be used directly by other passes.

static cl::opt<uint32_t> LikelyBranchWeight(
    "likely-branch-weight", cl::Hidden, cl::init(2000),
    cl::desc("Weight of the branch likely to be taken (default = 2000)"));
static cl::opt<uint32_t> UnlikelyBranchWeight(
    "unlikely-branch-weight", cl::Hidden, cl::init(1),
    cl::desc("Weight of the branch unlikely to be taken (default = 1)"));

static bool handleSwitchExpect(SwitchInst &SI) {
  CallInst *CI = dyn_cast<CallInst>(SI.getCondition());
  if (!CI)
    return false;

  Function *Fn = CI->getCalledFunction();
  if (!Fn || Fn->getIntrinsicID() != Intrinsic::expect)
    return false;

  Value *ArgValue = CI->getArgOperand(0);
  ConstantInt *ExpectedValue = dyn_cast<ConstantInt>(CI->getArgOperand(1));
  if (!ExpectedValue)
    return false;

  SwitchInst::CaseHandle Case = *SI.findCaseValue(ExpectedValue);
  unsigned n = SI.getNumCases(); // +1 for default case.
  SmallVector<uint32_t, 16> Weights(n + 1, UnlikelyBranchWeight);

  if (Case == *SI.case_default())
    Weights[0] = LikelyBranchWeight;
  else
    Weights[Case.getCaseIndex() + 1] = LikelyBranchWeight;

  SI.setMetadata(LLVMContext::MD_prof,
                 MDBuilder(CI->getContext()).createBranchWeights(Weights));

  SI.setCondition(ArgValue);
  return true;
}

// Handle both BranchInst and SelectInst.
template <class BrSelInst> static bool handleBrSelExpect(BrSelInst &BSI) {

  // Handle non-optimized IR code like:
  //   %expval = call i64 @llvm.expect.i64(i64 %conv1, i64 1)
  //   %tobool = icmp ne i64 %expval, 0
  //   br i1 %tobool, label %if.then, label %if.end
  //
  // Or the following simpler case:
  //   %expval = call i1 @llvm.expect.i1(i1 %cmp, i1 1)
  //   br i1 %expval, label %if.then, label %if.end

  CallInst *CI;

  ICmpInst *CmpI = dyn_cast<ICmpInst>(BSI.getCondition());
  CmpInst::Predicate Predicate;
  ConstantInt *CmpConstOperand = nullptr;
  if (!CmpI) {
    CI = dyn_cast<CallInst>(BSI.getCondition());
    Predicate = CmpInst::ICMP_NE;
  } else {
    Predicate = CmpI->getPredicate();
    if (Predicate != CmpInst::ICMP_NE && Predicate != CmpInst::ICMP_EQ)
      return false;

    CmpConstOperand = dyn_cast<ConstantInt>(CmpI->getOperand(1));
    if (!CmpConstOperand)
      return false;
    CI = dyn_cast<CallInst>(CmpI->getOperand(0));
  }

  if (!CI)
    return false;

  uint64_t ValueComparedTo = 0;
  if (CmpConstOperand) {
    if (CmpConstOperand->getBitWidth() > 64)
      return false;
    ValueComparedTo = CmpConstOperand->getZExtValue();
  }

  Function *Fn = CI->getCalledFunction();
  if (!Fn || Fn->getIntrinsicID() != Intrinsic::expect)
    return false;

  Value *ArgValue = CI->getArgOperand(0);
  ConstantInt *ExpectedValue = dyn_cast<ConstantInt>(CI->getArgOperand(1));
  if (!ExpectedValue)
    return false;

  MDBuilder MDB(CI->getContext());
  MDNode *Node;

  if ((ExpectedValue->getZExtValue() == ValueComparedTo) ==
      (Predicate == CmpInst::ICMP_EQ))
    Node = MDB.createBranchWeights(LikelyBranchWeight, UnlikelyBranchWeight);
  else
    Node = MDB.createBranchWeights(UnlikelyBranchWeight, LikelyBranchWeight);

  BSI.setMetadata(LLVMContext::MD_prof, Node);

  if (CmpI)
    CmpI->setOperand(0, ArgValue);
  else
    BSI.setCondition(ArgValue);
  return true;
}

static bool handleBranchExpect(BranchInst &BI) {
  if (BI.isUnconditional())
    return false;

  return handleBrSelExpect<BranchInst>(BI);
}

static bool lowerExpectIntrinsic(Function &F) {
  bool Changed = false;

  for (BasicBlock &BB : F) {
    // Create "block_weights" metadata.
    if (BranchInst *BI = dyn_cast<BranchInst>(BB.getTerminator())) {
      if (handleBranchExpect(*BI))
        ExpectIntrinsicsHandled++;
    } else if (SwitchInst *SI = dyn_cast<SwitchInst>(BB.getTerminator())) {
      if (handleSwitchExpect(*SI))
        ExpectIntrinsicsHandled++;
    }

    // Remove llvm.expect intrinsics. Iterate backwards in order
    // to process select instructions before the intrinsic gets
    // removed.
    for (auto BI = BB.rbegin(), BE = BB.rend(); BI != BE;) {
      Instruction *Inst = &*BI++;
      CallInst *CI = dyn_cast<CallInst>(Inst);
      if (!CI) {
        if (SelectInst *SI = dyn_cast<SelectInst>(Inst)) {
          if (handleBrSelExpect(*SI))
            ExpectIntrinsicsHandled++;
        }
        continue;
      }

      Function *Fn = CI->getCalledFunction();
      if (Fn && Fn->getIntrinsicID() == Intrinsic::expect) {
        Value *Exp = CI->getArgOperand(0);
        CI->replaceAllUsesWith(Exp);
        CI->eraseFromParent();
        Changed = true;
      }
    }
  }

  return Changed;
}

PreservedAnalyses LowerExpectIntrinsicPass::run(Function &F,
                                                FunctionAnalysisManager &) {
  if (lowerExpectIntrinsic(F))
    return PreservedAnalyses::none();

  return PreservedAnalyses::all();
}

namespace {
/// \brief Legacy pass for lowering expect intrinsics out of the IR.
///
/// When this pass is run over a function it uses expect intrinsics which feed
/// branches and switches to provide branch weight metadata for those
/// terminators. It then removes the expect intrinsics from the IR so the rest
/// of the optimizer can ignore them.
class LowerExpectIntrinsic : public FunctionPass {
public:
  static char ID;
  LowerExpectIntrinsic() : FunctionPass(ID) {
    initializeLowerExpectIntrinsicPass(*PassRegistry::getPassRegistry());
  }

  bool runOnFunction(Function &F) override { return lowerExpectIntrinsic(F); }
};
}

char LowerExpectIntrinsic::ID = 0;
INITIALIZE_PASS(LowerExpectIntrinsic, "lower-expect",
                "Lower 'expect' Intrinsics", false, false)

FunctionPass *llvm::createLowerExpectIntrinsicPass() {
  return new LowerExpectIntrinsic();
}
<|endoftext|>